Merge branch 'master' into epic-56039/LV-Evaluierung

# Conflicts:
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
This commit is contained in:
Cristina
2025-11-13 10:44:39 +01:00
104 changed files with 9173 additions and 1634 deletions
+15
View File
@@ -336,3 +336,18 @@ $config['navigation_menu']['system/issues/Issues/*'] = array(
'requiredPermissions' => array('admin:rw')
),
);
$config['navigation_menu']['apps'] = [
'stv' => [
'link' => site_url('studentenverwaltung'),
'description' => 'Studierendenverwaltung',
#'icon' => 'users',
'requiredPermissions' => array('admin:r', 'assistenz:r')
],
'lvv' => [
'link' => site_url('lVVerwaltung'),
'description' => 'LV Verwaltung',
#'icon' => 'person-chalkboard',
'requiredPermissions' => array('admin:r', 'assistenz:r')
]
];
+5 -5
View File
@@ -1,6 +1,6 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
if (! defined('BASEPATH')) exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
@@ -51,7 +51,7 @@ defined('BASEPATH') OR exit('No direct script access allowed');
| my-controller/my-method -> my_controller/my_method
*/
$route['default_controller'] = defined('CIS4') && CIS4 ? 'Cis4' : 'Vilesci';
$route['translate_uri_dashes'] = FALSE;
$route['translate_uri_dashes'] = false;
// Class name conflicts
$route['api/v1/organisation/[S|s]tudiengang/(:any)'] = 'api/v1/organisation/studiengang2/$1';
@@ -71,9 +71,9 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv
// (studiensemester_kurzbz)/inout[/(incoming|outgoing|gemeinsamestudien)]
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout'] = 'api/frontend/v1/stv/Students/index';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/incoming'] = 'api/frontend/v1/stv/Students/getIncoming';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/outgoing'] = 'api/frontend/v1/stv/Students/getOutgoing';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/gemeinsamestudien'] = 'api/frontend/v1/stv/Students/getGemeinsamestudien';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/incoming'] = 'api/frontend/v1/stv/Students/getIncoming/$1';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/outgoing'] = 'api/frontend/v1/stv/Students/getOutgoing/$1';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/gemeinsamestudien'] = 'api/frontend/v1/stv/Students/getGemeinsamestudien/$1';
// (studiengang_kz)/prestudent[/(studiensemester_kurzbz)[/(filter)[/(otherfilter)]]]
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudents/$1';
+2 -2
View File
@@ -29,7 +29,7 @@ $config['similar'] = [
$config['vector'] = [
'priority' => 1,
'rank' => "ts_rank({field}, to_tsquery('simple', {word}))",
'compare' => "to_tsquery('simple', {word}) @@ {field}"
'rank' => "ts_rank({field}, plainto_tsquery('simple', {word}))",
'compare' => "plainto_tsquery('simple', {word}) @@ {field}"
];
+38
View File
@@ -7,5 +7,43 @@ $CI =& get_instance();
$config['student'] = $CI->config->item('student', 'search');
$config['student']['searchfields']['pkz'] = [
'alias' => ['personenkennzeichen', 'personalid'],
'comparison' => 'equals',
'field' => 'matrikelnr'
];
$config['student']['searchfields']['matrnr'] = [
'alias' => ['matrikelnr', 'matrikelnummer', 'matrno', 'matriculationno', 'matriculationnumber', 'studno', 'studentno', 'studentnumber'],
'comparison' => 'equals',
'field' => 'matr_nr',
'join' => [
[
'table' => "public.tbl_prestudent",
'using' => "prestudent_id"
],
[
'table' => "public.tbl_person",
'using' => "person_id"
]
]
];
$config['prestudent'] = $CI->config->item('prestudent', 'search');
$config['prestudent']['searchfields']['pkz'] = [
'alias' => ['personenkennzeichen', 'personalid'],
'comparison' => 'equals',
'field' => 'matrikelnr',
'join' => [
'table' => "public.tbl_student",
'using' => "prestudent_id"
]
];
$config['prestudent']['searchfields']['matrnr'] = [
'alias' => ['matrikelnr', 'matrikelnummer', 'matrno', 'matriculationno', 'matriculationnumber', 'studno', 'studentno', 'studentnumber'],
'comparison' => 'equals',
'field' => 'matr_nr',
'join' => [
'table' => "public.tbl_person",
'using' => "person_id"
]
];
+7 -1
View File
@@ -63,7 +63,7 @@ $config['tabs'] =
'showCountNotes' => true
]
];
// List of fields to show when ZGV_DOKTOR_ANZEIGEN is defined
$fieldsZgvDoktor = ['zgvdoktorort', 'zgvdoktordatum', 'zgvdoktornation', 'zgvdoktor_erfuellt', 'zgvdoktor_code'];
@@ -84,6 +84,11 @@ if (!defined('ZGV_DOKTOR_ANZEIGEN') || !ZGV_DOKTOR_ANZEIGEN) {
);
}
$config['tabs']['projektarbeit']['defaultProjektbetreuerStunden'] = '4.0';
$config['tabs']['projektarbeit']['defaultProjektbetreuerStundenDiplom'] = '5.0';
$config['tabs']['projektarbeit']['lvLektroinnenzuteilungFixangestelltStundensatz'] = true;
$config['tabs']['projektarbeit']['defaultProjektbetreuerStundensatz'] = '80.0';
$config['student_tab_order'] = [
'details',
'notes',
@@ -97,6 +102,7 @@ $config['student_tab_order'] = [
'grades',
'exam',
'exemptions',
'projektarbeit',
'finalexam',
'mobility',
'jointstudies',
+1 -1
View File
@@ -72,7 +72,7 @@ class Documents extends Auth_Controller
$stgs = [];
$stsemArray = [];
$buchungstypen = implode('\',\'', defined("CIS_DOKUMENTE_STUDIENBEITRAG_TYPEN") ? unserialize(CIS_DOKUMENTE_STUDIENBEITRAG_TYPEN) : []);
$buchungstypen = defined("CIS_DOKUMENTE_STUDIENBEITRAG_TYPEN") ? unserialize(CIS_DOKUMENTE_STUDIENBEITRAG_TYPEN) : [];
$person_ids = [];
foreach ($stati as $status) {
$person_ids[] = $status->person_id;
@@ -637,7 +637,7 @@ class ProfilUpdate extends FHCAPI_Controller
//? Send email to the Studiengangsassistentinnen
$this->StudentModel->addSelect(["public.tbl_studiengang.email"]);
$this->StudentModel->addJoin("public.tbl_benutzer", "public.tbl_benutzer.uid = public.tbl_student.student_uid");
$this->StudentModel->addJoin("public.tbl_prestudent", "public.tbl_benutzer.person_id = public.tbl_prestudent.person_id");
$this->StudentModel->addJoin("public.tbl_prestudent", "public.tbl_benutzer.person_id = public.tbl_prestudent.person_id and public.tbl_student.studiengang_kz = public.tbl_prestudent.studiengang_kz");
$this->StudentModel->addJoin("public.tbl_prestudentstatus", "public.tbl_prestudentstatus.prestudent_id = public.tbl_prestudent.prestudent_id");
$this->StudentModel->addJoin("public.tbl_studiengang", "public.tbl_studiengang.studiengang_kz = public.tbl_prestudent.studiengang_kz");
$this->StudentModel->addGroupBy(["public.tbl_studiengang.email"]);
@@ -16,7 +16,7 @@ class Messages extends FHCAPI_Controller
'getNameOfDefaultRecipient' => ['admin:r', 'assistenz:r'],
'sendMessage' => ['admin:r', 'assistenz:r'],
'deleteMessage' => ['admin:r', 'assistenz:r'],
'getVorlagentext' => ['admin:r', 'assistenz:r'],
'getDataVorlage' => ['admin:r', 'assistenz:r'],
'getPreviewText' => ['admin:r', 'assistenz:r'],
'getReplyData' => ['admin:r', 'assistenz:r'],
'getPersonId' => ['admin:r', 'assistenz:r'],
@@ -52,11 +52,14 @@ class Messages extends FHCAPI_Controller
$result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
$data = $this->getDataOrTerminateWithError($result);
if (hasData($result))
{
$data = getData($result);
$this->addMeta('count', $data['count']);
$this->terminateWithSuccess($data['data']);
}
$this->addMeta('count', $data['count']);
$this->terminateWithSuccess($data['data']);
$this->terminateWithSuccess(array());
}
public function getVorlagen()
@@ -66,33 +69,23 @@ class Messages extends FHCAPI_Controller
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$result = $this->BenutzerfunktionModel->getBenutzerfunktionByUid($uid, 'oezuordnung');
$data = $this->getDataOrTerminateWithError($result);
$oe_kurzbz = current($data);
if (hasData($result))
{
$this->load->model('system/Vorlage_model', 'VorlageModel');
$this->load->model('system/Vorlage_model', 'VorlageModel');
$data = getData($result);
$result = $this->VorlageModel->getAllVorlagenByOe($oe_kurzbz->oe_kurzbz);
$data = $this->getDataOrTerminateWithError($result);
$oe_kurzbz = array_column($data, 'oe_kurzbz');
$result = $this->VorlageModel->getAllVorlagenByOe($oe_kurzbz);
$this->terminateWithSuccess($data);
$this->terminateWithSuccess(hasData($result) ? getData($result) : array());
}
//If admin
$this->VorlageModel->addOrder('vorlage_kurzbz', 'ASC');
$result = $this->VorlageModel->loadWhere(
array(
'mimetype' => 'text/html'
));
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
$this->terminateWithSuccess(array());
}
public function getVorlagentext($vorlage_kurzbz)
public function getDataVorlage($vorlage_kurzbz)
{
//$this->terminateWithError("vor " . $vorlage_kurzbz, self::ERROR_TYPE_GENERAL);
//$studiengang_kz = 227; //TODO(Manu) dynamisieren NULL
$studiengang_kz = 0;
$this->load->model('system/Vorlagestudiengang_model', 'VorlagestudiengangModel');
$this->VorlagestudiengangModel->addOrder('version', 'DESC');
@@ -104,12 +97,8 @@ class Messages extends FHCAPI_Controller
]);
$data = $this->getDataOrTerminateWithError($result);
//not correct with Vorlage
$vorlage = current($data);
//$this->terminateWithSuccess($data);
$this->terminateWithSuccess($vorlage->text);
$this->terminateWithSuccess($vorlage);
}
public function getMessageVarsPerson($id, $typeId)
@@ -154,7 +143,7 @@ class Messages extends FHCAPI_Controller
public function sendMessage($recipient_id)
{
//has to be uid
// $this->terminateWithError("uid", $recipient_id, self::ERROR_TYPE_GENERAL);
// $this->terminateWithError("uid", $recipient_id, self::ERROR_TYPE_GENERAL);
//default setting
$receiversPersonId = $this->_getPersonId($recipient_id, 'uid');
@@ -223,8 +212,6 @@ class Messages extends FHCAPI_Controller
}
elseif($typeId == 'prestudent_id')
{
// $this->terminateWithError("prestudent_id ", self::ERROR_TYPE_GENERAL);
$result = $this->MessagesModel->parseMessageTextPrestudent($id, $body);
$bodyParsed = $this->getDataOrTerminateWithError($result);
}
@@ -429,7 +416,7 @@ class Messages extends FHCAPI_Controller
private function _getPrestudentIdFromUid($uid)
{
// $this->terminateWithError($uid, self::ERROR_TYPE_GENERAL);
// $this->terminateWithError($uid, self::ERROR_TYPE_GENERAL);
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->loadWhere(
['student_uid' => $uid]
@@ -463,4 +450,4 @@ class Messages extends FHCAPI_Controller
date_format(date_create($sentDate), 'd.m.Y H:i'), $receiverName, $receiverSurname, $body
);
}
}
}
@@ -20,8 +20,6 @@ class Abschlusspruefung extends FHCAPI_Controller
'getBeurteilungen' => ['admin:rw', 'assistenz:rw'],
'getAkadGrade' => ['admin:rw', 'assistenz:rw'],
'getMitarbeiter' => ['admin:rw', 'assistenz:rw'],
'getAllMitarbeiter' => ['admin:rw', 'assistenz:rw'],
'getAllPersons' => ['admin:rw', 'assistenz:rw'],
'getPruefer' => ['admin:rw', 'assistenz:rw'],
'getTypStudiengang' => ['admin:rw', 'assistenz:rw'],
'checkForExistingExams' => ['admin:rw', 'assistenz:rw'],
@@ -102,35 +100,45 @@ class Abschlusspruefung extends FHCAPI_Controller
{
$abschlusspruefung_id = $this->input->post('id');
$this->AbschlusspruefungModel->addSelect('lehre.tbl_abschlusspruefung.*');
$this->AbschlusspruefungModel->addSelect("
CASE
WHEN pruefer1 IS NOT NULL
THEN CONCAT(p1.nachname, ' ', p1.vorname, COALESCE(' ' || p1.titelpre, ''))
ELSE NULL
END AS p1
");
$this->AbschlusspruefungModel->addSelect("
CASE
WHEN pruefer2 IS NOT NULL
THEN CONCAT(p2.nachname, ' ', p2.vorname, COALESCE(' ' || p2.titelpre, ''))
ELSE NULL
END AS p2
");
$this->AbschlusspruefungModel->addSelect("
CASE
WHEN pruefer3 IS NOT NULL
THEN CONCAT(p3.nachname, ' ', p3.vorname, COALESCE(' ' || p3.titelpre, ''))
ELSE NULL
END AS p3
");
$this->AbschlusspruefungModel->addSelect("
CASE
WHEN vorsitz IS NOT NULL
THEN CONCAT(pv.nachname, ' ', pv.vorname, COALESCE(' ' || pv.titelpre, ''), ' (', ben.uid , ')' )
ELSE NULL
END AS pv
");
$this->AbschlusspruefungModel->addSelect(
'lehre.tbl_abschlusspruefung.*,
p1.person_id AS p1_person_id, p1.vorname AS p1_vorname, p1.nachname AS p1_nachname,
p1.titelpre AS p1_titelpre, p1.titelpost AS p1_titelpost,
p2.person_id AS p2_person_id, p2.vorname AS p2_vorname, p2.nachname AS p2_nachname,
p2.titelpre AS p2_titelpre, p2.titelpost AS p2_titelpost,
p3.person_id AS p3_person_id, p3.vorname AS p3_vorname, p3.nachname AS p3_nachname,
p3.titelpre AS p3_titelpre, p3.titelpost AS p3_titelpost,
pv.person_id AS pv_person_id, pv.vorname AS pv_vorname, pv.nachname AS pv_nachname,
pv.titelpre AS pv_titelpre, pv.titelpost AS pv_titelpost, ben.uid AS pv_uid'
);
//~ $this->AbschlusspruefungModel->addSelect("
//~ CASE
//~ WHEN pruefer1 IS NOT NULL
//~ THEN CONCAT(p1.nachname, ' ', p1.vorname, COALESCE(' ' || p1.titelpre, ''))
//~ ELSE NULL
//~ END AS p1
//~ ");
//~ $this->AbschlusspruefungModel->addSelect("
//~ CASE
//~ WHEN pruefer2 IS NOT NULL
//~ THEN CONCAT(p2.nachname, ' ', p2.vorname, COALESCE(' ' || p2.titelpre, ''))
//~ ELSE NULL
//~ END AS p2
//~ ");
//~ $this->AbschlusspruefungModel->addSelect("
//~ CASE
//~ WHEN pruefer3 IS NOT NULL
//~ THEN CONCAT(p3.nachname, ' ', p3.vorname, COALESCE(' ' || p3.titelpre, ''))
//~ ELSE NULL
//~ END AS p3
//~ ");
//~ $this->AbschlusspruefungModel->addSelect("
//~ CASE
//~ WHEN vorsitz IS NOT NULL
//~ THEN CONCAT(pv.nachname, ' ', pv.vorname, COALESCE(' ' || pv.titelpre, ''), ' (', ben.uid , ')' )
//~ ELSE NULL
//~ END AS pv
//~ ");
$this->AbschlusspruefungModel->addJoin('public.tbl_benutzer ben', 'ON (ben.uid = lehre.tbl_abschlusspruefung.vorsitz)', 'LEFT');
$this->AbschlusspruefungModel->addJoin('public.tbl_person pv', 'ON (pv.person_id = ben.person_id)', 'LEFT');
$this->AbschlusspruefungModel->addJoin('public.tbl_person p1', 'ON (p1.person_id = lehre.tbl_abschlusspruefung.pruefer1)', 'LEFT');
@@ -220,8 +228,10 @@ class Abschlusspruefung extends FHCAPI_Controller
$this->terminateWithSuccess($typStudiengang);
}
public function getMitarbeiter($searchString)
public function getMitarbeiter()
{
$searchString = $this->input->get('searchString') ?? '';
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$result = $this->MitarbeiterModel->searchMitarbeiter($searchString, 'mitAkadGrad');
@@ -232,8 +242,10 @@ class Abschlusspruefung extends FHCAPI_Controller
$this->terminateWithSuccess($result ?: []);
}
public function getPruefer($searchString)
public function getPruefer()
{
$searchString = $this->input->get('searchString') ?? '';
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$result = $this->MitarbeiterModel->searchMitarbeiter($searchString, 'ohneMaUid');
@@ -444,58 +456,4 @@ class Abschlusspruefung extends FHCAPI_Controller
}
$this->terminateWithSuccess('step3');
}
/*
* returns list of all Mitarbeiter
* as key value list to be used in select or autocomplete
*/
public function getAllMitarbeiter()
{
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$sql = "
SELECT
ma.mitarbeiter_uid as mitarbeiter_uid,
CONCAT(p.nachname, ' ', p.vorname, ' (', ma.mitarbeiter_uid, ')') as label
FROM
public.tbl_mitarbeiter ma
JOIN public.tbl_benutzer bn ON (bn.uid = ma.mitarbeiter_uid)
JOIN public.tbl_person p ON (p.person_id = bn.person_id)
ORDER BY
p.nachname ASC
";
$result = $this->MitarbeiterModel->execReadOnlyQuery($sql);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
/*
* returns list of all Persons
* as key value list to be used in select or autocomplete
*/
public function getAllPersons()
{
$this->load->model('person/Person_model', 'PersonModel');
$sql = "
SELECT
p.vorname, p.nachname, p.person_id,
CONCAT(p.nachname, ' ', p.vorname) as label
FROM
public.tbl_person p
-- JOIN public.tbl_benutzer bn ON (p.person_id = bn.person_id)
-- and bn.aktiv = 'true'
ORDER BY
p.nachname ASC
";
//TODO(manu) check if filter active benutzer
$result = $this->PersonModel->execReadOnlyQuery($sql);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -39,7 +39,7 @@ class Archiv extends FHCAPI_Controller
'archive' => ['admin:w', 'assistenz:w'],
'download' => ['admin:w', 'assistenz:w'],
'update' => ['admin:w'],
'delete' => ['admin:w', 'assistenz:w']
'delete' => ['admin:w', 'assistenz:w'],
]);
// Load models
@@ -107,13 +107,9 @@ class Archiv extends FHCAPI_Controller
$result = $this->AkteModel->load($akte_id);
if (!hasData($result)) $this->terminateWithError('Akte not found');
$data = $this->getDataOrTerminateWithError($result);
$data = getData($result)[0];
//$this->addMeta("daa", $data->inhalt);
$fileObj = new stdClass();
if (isset($data->inhalt) && $data->inhalt != '')
@@ -133,12 +129,7 @@ class Archiv extends FHCAPI_Controller
//header("Content-type: $data->mimetype");
header('Content-Disposition: attachment; filename="'.$data->titel.'"');
readfile($filename);
//echo base64_decode($data->inhalt);
die();
//~ $fileObj->file = $data->inhalt;
//~ $fileObj->name = $data->titel;
//~ $fileObj->mimetype = $data->mimetype;
//~ $fileObj->disposition = 'attachment';
}
else
{
@@ -146,12 +137,6 @@ class Archiv extends FHCAPI_Controller
$result = $this->aktelib->get($akte_id);
}
/* $fileObj->filename
* $fileObj->file
* $fileObj->name
* $fileObj->mimetype
* $fileObj->disposition*/
}
/**
@@ -33,6 +33,7 @@ class Config extends FHCAPI_Controller
{
// TODO(chris): permissions
parent::__construct([
'filter' => ['admin:r', 'assistenz:r'],
'student' => ['admin:r', 'assistenz:r'],
'students' => ['admin:r', 'assistenz:r']
]);
@@ -45,13 +46,166 @@ class Config extends FHCAPI_Controller
'lehre',
'stv',
'konto',
'abschlusspruefung'
'abschlusspruefung',
'projektarbeit'
]);
// Load Config
$this->load->config('stv');
}
/**
* Get the config for the student filters
*
* @return void
*/
public function filter()
{
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$this->load->model('crm/Buchungstyp_model', 'BuchungstypModel');
$this->BuchungstypModel->addOrder('beschreibung');
$result = $this->BuchungstypModel->load();
$buchungstyp_kurzbz = $this->getDataOrTerminateWithError($result);
$buchungstyp_kurzbz_plus_all = array_merge([[
'buchungstyp_kurzbz' => 'all',
'beschreibung' => $this->p->t('stv', 'konto_all_types')
]], $buchungstyp_kurzbz);
$this->load->model('crm/Statusgrund_model', 'StatusgrundModel');
$result = $this->StatusgrundModel->getAktiveGruende();
$statusgruende = $this->getDataOrTerminateWithError($result);
$result = [];
$result[] = [
'id' => 'filter_konto_count_0',
'label' => $this->p->t('stv', 'filter_konto_count_0'),
'type' => 'konto',
'fixed' => [
'missing' => true,
'usestdsem' => true
],
'dynamic' => [
'buchungstyp_kurzbz' => [
'type' => 'select',
'values' => $buchungstyp_kurzbz,
'value_key' => 'buchungstyp_kurzbz',
'label_key' => 'beschreibung'
]
]
];
$result[] = [
'id' => 'filter_konto_missing_counter',
'label' => $this->p->t('stv', 'filter_konto_missing_counter'),
'type' => 'konto_counter',
'dynamic' => [
'buchungstyp_kurzbz' => [
'type' => 'select',
'values' => $buchungstyp_kurzbz_plus_all,
'value_key' => 'buchungstyp_kurzbz',
'label_key' => 'beschreibung'
],
'samestg' => [
'type' => 'bool',
'label' => $this->p->t('stv', 'filter_konto_samestg'),
'default' => $this->variablelib->getVar('kontofilterstg') == 'true'
]
]
];
$result[] = [
'id' => 'filter_documents',
'label' => $this->p->t('stv', 'filter_documents'),
'type' => 'documents'
];
$result[] = [
'id' => 'filter_konto_missing_counter_past',
'label' => $this->p->t('stv', 'filter_konto_missing_counter_past'),
'type' => 'konto_counter',
'fixed' => [
'past' => true
],
'dynamic' => [
'buchungstyp_kurzbz' => [
'type' => 'select',
'values' => $buchungstyp_kurzbz_plus_all,
'value_key' => 'buchungstyp_kurzbz',
'label_key' => 'beschreibung'
],
'samestg' => [
'type' => 'bool',
'label' => $this->p->t('stv', 'filter_konto_samestg'),
'default' => $this->variablelib->getVar('kontofilterstg') == 'true'
]
]
];
$result[] = [
'id' => 'filter_konto_missing_studiengebuehr',
'label' => $this->p->t('stv', 'filter_konto_missing_studiengebuehr'),
'type' => 'konto',
'fixed' => [
'missing' => true,
'usestdsem' => true
],
'dynamic' => [
'buchungstyp_kurzbz' => [
'type' => 'select',
'values' => $buchungstyp_kurzbz,
'value_key' => 'buchungstyp_kurzbz',
'label_key' => 'beschreibung'
]
]
];
$result[] = [
'id' => 'filter_konto_studiengebuehrerhoeht',
'label' => $this->p->t('stv', 'filter_konto_studiengebuehrerhoeht'),
'type' => 'konto',
'fixed' => [
'usestdsem' => true
],
'dynamic' => [
'buchungstyp_kurzbz' => [
'type' => 'select',
'values' => $buchungstyp_kurzbz,
'value_key' => 'buchungstyp_kurzbz',
'label_key' => 'beschreibung'
]
]
];
$result[] = [
'id' => 'filter_zgv_without_date',
'label' => $this->p->t('stv', 'filter_zgv_without_date'),
'type' => 'zgv'
];
$result[] = [
'id' => 'filter_statusgrund',
'label' => $this->p->t('stv', 'filter_statusgrund'),
'type' => 'statusgrund',
'fixed' => [
'usestdsem' => true
],
'dynamic' => [
'statusgrund_id' => [
'type' => 'select',
'values' => $statusgruende,
'value_key' => 'statusgrund_id',
'label_key' => 'bezeichnung'
]
]
];
Events::trigger('stv_conf_filter', function & () use (&$result) {
return $result;
});
$this->terminateWithSuccess($result);
}
public function student()
{
$result = [];
@@ -59,21 +213,21 @@ class Config extends FHCAPI_Controller
$result['details'] = [
'title' => $this->p->t('stv', 'tab_details'),
'component' => './Stv/Studentenverwaltung/Details/Details.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Details.js'),
'config' => $config['details']
];
$result['notes'] = [
'title' => $this->p->t('stv', 'tab_notes'),
'component' => './Stv/Studentenverwaltung/Details/Notizen.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Notizen.js'),
'config' => $config['notes'],
'showSuffix' => ($config['notes']['showCountNotes'] ?? false),
'suffixhelper' => APP_ROOT . 'public/js/helpers/Stv/Studentenverwaltung/Details/Notizen/NotizenSuffixHelper.js'
'suffixhelper' => absoluteJsImportUrl('public/js/helpers/Stv/Studentenverwaltung/Details/Notizen/NotizenSuffixHelper.js')
];
$result['contact'] = [
'title' => $this->p->t('stv', 'tab_contact'),
'component' => './Stv/Studentenverwaltung/Details/Kontakt.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Kontakt.js'),
'config' => [
'showBankaccount' => $this->permissionlib->isBerechtigt('mitarbeiter/bankdaten')
|| $this->permissionlib->isBerechtigt('student/bankdaten')
@@ -81,20 +235,20 @@ class Config extends FHCAPI_Controller
];
$result['prestudent'] = [
'title' => $this->p->t('stv', 'tab_prestudent'),
'component' => './Stv/Studentenverwaltung/Details/Prestudent.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Prestudent.js'),
'config' => $config['prestudent']
];
$result['status'] = [
'title' => 'Status',
'component' => './Stv/Studentenverwaltung/Details/MultiStatus.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/MultiStatus.js')
];
$result['documents'] = [
'title' => $this->p->t('stv', 'tab_documents'),
'component' => './Stv/Studentenverwaltung/Details/Dokumente.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Dokumente.js')
];
$result['banking'] = [
'title' => $this->p->t('stv', 'tab_banking'),
'component' => './Stv/Studentenverwaltung/Details/Konto.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Konto.js'),
'config' => [
'showZahlungsbestaetigung' => (defined('ZAHLUNGSBESTAETIGUNG_ANZEIGEN') && ZAHLUNGSBESTAETIGUNG_ANZEIGEN),
'showBuchungsnr' => $this->permissionlib->isBerechtigt('admin'),
@@ -106,20 +260,23 @@ class Config extends FHCAPI_Controller
];
$result['resources'] = [
'title' => $this->p->t('stv', 'tab_resources'),
'component' => './Stv/Studentenverwaltung/Details/Betriebsmittel.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Betriebsmittel.js'),
'showOnlyWithUid' => true
];
$result['groups'] = [
'title' => $this->p->t('stv', 'tab_groups'),
'component' => './Stv/Studentenverwaltung/Details/Gruppen.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Groups.js'),
'showOnlyWithUid' => true
];
$result['messages'] = [
'title' => $this->p->t('stv', 'tab_messages'),
'component' => './Stv/Studentenverwaltung/Details/Messages.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Messages.js'),
'showOnlyWithUid' => true
];
$result['grades'] = [
'title' => $this->p->t('stv', 'tab_grades'),
'component' => './Stv/Studentenverwaltung/Details/Noten.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Noten.js'),
'showOnlyWithUid' => true,
'config' => [
'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE,
@@ -132,29 +289,42 @@ class Config extends FHCAPI_Controller
$result['exam'] = [
'title' => $this->p->t('stv', 'tab_exam'),
'component' => './Stv/Studentenverwaltung/Details/Pruefung.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Pruefung.js'),
'showOnlyWithUid' => true
];
$result['exemptions'] = [
'title' => $this->p->t('lehre', 'anrechnungen'),
'component' => './Stv/Studentenverwaltung/Details/Anrechnungen.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Anrechnungen.js'),
'config' => $config['exemptions']
];
$result['finalexam'] = [
'title' => $this->p->t('stv', 'tab_finalexam'),
'component' => './Stv/Studentenverwaltung/Details/Abschlusspruefung.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung.js'),
'showOnlyWithUid' => true,
'config' => $config['finalexam']
];
$result['projektarbeit'] = [
'title' => $this->p->t('stv', 'tab_projektarbeit'),
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit.js'),
'config' => array_merge(
$config['projektarbeit'],
['showVertragsdetails' =>
defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN') && FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN]
)
];
$result['mobility'] = [
'title' => $this->p->t('stv', 'tab_mobility'),
'component' => './Stv/Studentenverwaltung/Details/Mobility.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Mobility.js'),
'showOnlyWithUid' => true
];
$result['archive'] = [
'title' => $this->p->t('stv', 'tab_archive'),
'component' => './Stv/Studentenverwaltung/Details/Archiv.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Archiv.js'),
'config' => [
'showEdit' => $this->permissionlib->isBerechtigt('admin')
]
@@ -162,22 +332,24 @@ class Config extends FHCAPI_Controller
$result['jointstudies'] = [
'title' => $this->p->t('stv', 'tab_jointstudies'),
'component' => './Stv/Studentenverwaltung/Details/JointStudies.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/JointStudies.js'),
'showOnlyWithUid' => true
];
$result['coursedates'] = [
'title' => $this->p->t('stv', 'tab_courseDates'),
'component' => './Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine.js')
];
$result['admissionDates'] = [
'title' => $this->p->t('stv', 'tab_admissionDates'),
'component' => './Stv/Studentenverwaltung/Details/Aufnahmetermine.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Aufnahmetermine.js')
];
$result['functions'] = [
'title' => $this->p->t('stv', 'tab_functions'),
'component' => './Stv/Studentenverwaltung/Details/Funktionen.js'
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Funktionen.js'),
'showOnlyWithUid' => true
];
Events::trigger('stv_conf_student', function & () use (&$result) {
@@ -195,7 +367,7 @@ class Config extends FHCAPI_Controller
$config = $this->config->item('tabs');
$result['banking'] = [
'title' => $this->p->t('stv', 'tab_banking'),
'component' => './Stv/Studentenverwaltung/Details/Konto.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Konto.js'),
'config' => [
'showZahlungsbestaetigung' => (defined('ZAHLUNGSBESTAETIGUNG_ANZEIGEN') && ZAHLUNGSBESTAETIGUNG_ANZEIGEN),
'showBuchungsnr' => $this->permissionlib->isBerechtigt('admin'),
@@ -205,9 +377,14 @@ class Config extends FHCAPI_Controller
'additionalCols' => []
]
];
$result['groups'] = [
'title' => $this->p->t('stv', 'tab_groups'),
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Groups.js'),
'showOnlyWithUid' => true
];
$result['status'] = [
'title' => 'Status',
'component' => './Stv/Studentenverwaltung/Details/MultiStatus.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/MultiStatus.js'),
'config' => [
'changeStatusToAbbrecherStgl' => $this->permissionlib->isBerechtigt('admin'),
'changeStatusToAbbrecherStud' => $this->permissionlib->isBerechtigt('admin'),
@@ -218,17 +395,23 @@ class Config extends FHCAPI_Controller
];
$result['finalexam'] = [
'title' => $this->p->t('stv', 'tab_finalexam'),
'component' => './Stv/Studentenverwaltung/Details/Abschlusspruefung.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung.js'),
'showOnlyWithUid' => true,
'config' => $config['finalexam']
];
$result['archive'] = [
'title' => $this->p->t('stv', 'tab_archive'),
'component' => './Stv/Studentenverwaltung/Details/Archiv.js',
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Archiv.js'),
'config' => [
'showEdit' => $this->permissionlib->isBerechtigt('admin')
]
];
$result['kontaktieren'] = [
'title' => $this->p->t('stv', 'tab_kontaktieren'),
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js'),
];
Events::trigger('stv_conf_students', function & () use (&$result) {
return $result;
});
@@ -2,6 +2,7 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \CI3_Events as Events;
use \DateTime as DateTime;
class Dokumente extends FHCAPI_Controller
@@ -19,6 +20,8 @@ class Dokumente extends FHCAPI_Controller
'getDoktypen' => ['admin:r', 'assistenz:r'],
'uploadDokument' => ['admin:rw', 'assistenz:rw'],
'download' => ['admin:rw', 'assistenz:rw'],
'getDocumentDropDown' => ['admin:rw', 'assistenz:rw'],
'getDocumentDropDownMulti' => ['admin:rw', 'assistenz:rw'],
]);
// Load Libraries
@@ -566,4 +569,395 @@ class Dokumente extends FHCAPI_Controller
return false;
}
}
public function getDocumentDropDown($prestudent_id, $studiensemester_kurzbz, $studiengang_kz)
{
$this->load->helper('hlp_common');
//permission to create also odt, and doc outputs of certain documents(menu abschlusspruefung)
$hasPermissionOutputformat = $this->permissionlib->isBerechtigt('system/change_outputformat', 's');
if (!$prestudent_id)
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Prestudent_id']), self::ERROR_TYPE_GENERAL);
if (!$studiensemester_kurzbz)
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiensemester']), self::ERROR_TYPE_GENERAL);
if(!$studiengang_kz)
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiengang_kz']), self::ERROR_TYPE_GENERAL);
$uid = $this->_loadUIDFromPrestudent($prestudent_id);
$semArray = $this->_getEntriesStudiensemester();
$stgTyp = $this->_getStudiengangstyp($studiengang_kz);
$documents = [
buildDropdownEntryPrintArray("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uid, 10, null),
buildDropdownEntryPrintArray("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uid, 20, null),
buildDropdownEntryPrintArray("ausbildungsvertrag_en", "Ausbildungsvertrag Englisch", "xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf", $uid, 21, null),
buildDropdownEntryPrintArray("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uid, 40, null),
buildDropdownEntryPrintArray("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uid, 41, null),
buildDropdownEntryPrintArray("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uid", $uid,100, "zutrittskarte.php"),
buildDropdownEntryPrintArray("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uid, 60, null),
buildDropdownEntryPrintArray("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uid, 61, null),
$this->buildStudienerfolgSubmenu("de", $uid, $semArray, $studiensemester_kurzbz),
$this->buildStudienerfolgSubmenu("en", $uid, $semArray, $studiensemester_kurzbz),
$this->buildStudienerfolgSubmenu("de", $uid, $semArray, $studiensemester_kurzbz, true),
$this->buildStudienerfolgSubmenu("en", $uid, $semArray, $studiensemester_kurzbz, true),
[
"id" => "submenu_studstatus",
"type" => "submenu",
"name" => "Verwaltung des StudierendenStatus",
"order" => 110,
"data" => [
buildDropdownEntryPrintArray("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null),
buildDropdownEntryPrintArray("Abmeldung durch Stgl", "AntragAbmeldungStgl", "xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&prestudent_id=$prestudent_id&output=pdf", $uid, null, null),
buildDropdownEntryPrintArray("Unterbrechung", "Unterbrechung", "xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null),
buildDropdownEntryPrintArray("Wiederholung", "Abmeldung durch Ablauf der Wiederholungsfrist", "xml=AntragWiederholung.xml.php&xsl=AntragWiederholung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null),
]
],
$this->loadDropDownEntriesFinalExam($hasPermissionOutputformat, $stgTyp, $uid),
buildDropdownEntryPrintArray("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uid, 80, null),
buildDropdownEntryPrintArray("diplomasupp", "Diploma Supplement (nur Voransicht)", "xml=diplomasupplement.xml.php&xsl_stg_kz=$studiengang_kz&xsl=DiplSupplement&output=pdf", $uid, 81, null)
];
Events::trigger('DocumentGenerationDropDown',
// passing $menu per reference
function & () use (&$documents) {
return $documents;
},
$prestudent_id,
$studiensemester_kurzbz,
$studiengang_kz
);
usort($documents, function ($a, $b) {
$orderA = isset($a['order']) ? (int)$a['order'] : PHP_INT_MAX;
$orderB = isset($b['order']) ? (int)$b['order'] : PHP_INT_MAX;
return $orderA <=> $orderB;
});
$this->terminateWithSuccess($documents);
return $documents || null;
}
public function getDocumentDropDownMulti($studiensemester_kurzbz,$studiengang_kz)
{
//permission to create also odt, and doc outputs of certain documents (menu abschlusspruefung)
$hasPermissionOutputformat = $this->permissionlib->isBerechtigt('system/change_outputformat', 's');
$studentUids = $this->input->get('studentUids');
$prestudentIds = [];
if (is_array($studentUids) && !empty($studentUids)) {
foreach ($studentUids as $uid) {
$prestudent_id = $this-> _loadPrestudentFromUid($uid);
$prestudentIds[] = $prestudent_id;
}
}
else
{
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Array StudentUIDs']), self::ERROR_TYPE_GENERAL);
}
if (!$studiensemester_kurzbz)
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiensemester']), self::ERROR_TYPE_GENERAL);
if(!$studiengang_kz)
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiengang_kz']), self::ERROR_TYPE_GENERAL);
$uidString = implode(";", $studentUids);
$prestudentIdsString = implode(";", $prestudentIds);
$semArray = $this->_getEntriesStudiensemester();
$stgTyp = $this->_getStudiengangstyp($studiengang_kz);
$documents = [
buildDropdownEntryPrintArray("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uidString, 10, null),
buildDropdownEntryPrintArray("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uidString, 20, null),
buildDropdownEntryPrintArray("ausbildungsvertrag_en", "Ausbildungsvertrag Englisch", "xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf", $uidString, 21, null),
buildDropdownEntryPrintArray("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uidString, 40, null),
buildDropdownEntryPrintArray("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uidString, 41, null),
buildDropdownEntryPrintArray("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uidString", $uidString,100, "zutrittskarte.php"),
buildDropdownEntryPrintArray("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uidString, 60, null),
buildDropdownEntryPrintArray("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uidString, 61, null),
// Studienerfolg Menüs automatisch
$this->buildStudienerfolgSubmenu("de", $uidString, $semArray, $studiensemester_kurzbz),
$this->buildStudienerfolgSubmenu("en", $uidString, $semArray, $studiensemester_kurzbz),
$this->buildStudienerfolgSubmenu("de", $uidString, $semArray, $studiensemester_kurzbz, true),
$this->buildStudienerfolgSubmenu("en", $uidString, $semArray, $studiensemester_kurzbz, true),
[
"id" => "submenu_studstatus",
"type" => "submenu",
"name" => "Verwaltung des StudierendenStatus",
"order" => 110,
"data" => [
buildDropdownEntryPrintArray("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null),
buildDropdownEntryPrintArray("Abmeldung durch Stgl", "AntragAbmeldungStgl", "xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null),
buildDropdownEntryPrintArray("Unterbrechung", "Unterbrechung", "xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null),
buildDropdownEntryPrintArray("Wiederholung", "Abmeldung durch Ablauf der Wiederholungsfrist", "xml=AntragWiederholung.xml.php&xsl=AntragWiederholung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null),
]
],
$this->loadDropDownEntriesFinalExam($hasPermissionOutputformat, $stgTyp, $uidString),
//TODO(Manu) also in Fas multi not working
/* buildDropdownEntryPrintArray("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uidString, 80, null),
*/
buildDropdownEntryPrintArray("diplomasupp", "Diploma Supplement (nur Voransicht)", "xml=diplomasupplement.xml.php&xsl_stg_kz=$studiengang_kz&xsl=DiplSupplement&output=pdf", $uidString, 81, null)
];
Events::trigger('DocumentGenerationDropDownMulti',
// passing $menu per reference
function & () use (&$documents) {
return $documents;
},
$studentUids,
$studiensemester_kurzbz,
$studiengang_kz
);
usort($documents, function ($a, $b) {
$orderA = isset($a['order']) ? (int)$a['order'] : PHP_INT_MAX;
$orderB = isset($b['order']) ? (int)$b['order'] : PHP_INT_MAX;
return $orderA <=> $orderB;
});
$this->terminateWithSuccess($documents);
return $documents || null;
}
private function _loadUIDFromPrestudent($prestudent_id)
{
if(!$prestudent_id){
return $this->terminateWithError("no prestudent ID received.");
}
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->loadWhere(
['prestudent_id' => $prestudent_id]
);
$data = $this->getDataOrTerminateWithError($result);
$student = current($data);
return $student->student_uid;
}
private function _loadPrestudentFromUid($studentUid)
{
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->loadWhere(
['student_uid' => $studentUid]
);
$data = $this->getDataOrTerminateWithError($result);
$student = current($data);
return $student->prestudent_id;
}
/**
* is building an array with studiensemesterkurzb
* actual studiensemester plus the 5 studiensemester in the past
* @return Array Studiensemester_kurzbz
*/
private function _getEntriesStudiensemester(){
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->StudiensemesterModel->addPlusMinus(1, 5);
$this->StudiensemesterModel->addOrder('ende', 'DESC');
$result = $this->StudiensemesterModel->load();
$data = $this->getDataOrTerminateWithError($result);
foreach($data as $sem)
{
$semArray[] = $sem->studiensemester_kurzbz;
}
array_shift($semArray);
return $semArray;
}
/**
* is returning the typ of Studiengang (Bakk oder Master)
* @return character eg. 'b' or 'm'
*/
private function _getStudiengangstyp($studiengang_kz)
{
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$result = $this->StudiengangModel->loadWhere(
array('studiengang_kz' => $studiengang_kz)
);
$data = $this->getDataOrTerminateWithError($result);
$typStudiengang = current($data)->typ;
return $typStudiengang;
}
/**
* helper function to create ArrayStructure
* actual studiensemester plus the 5 studiensemester in the past
* @return Array Studiensemester_kurzbz
*/
private function buildStudienerfolgSubmenu($lang, $uid, $semArray, $studiensemester_kurzbz, $fa = false)
{
$entries = [];
$xsl = $lang === "de" ? "Studienerfolg" : "StudienerfolgEng";
$idPrefix = "submenu_studienerfolg_" . $lang . ($fa ? "_fa" : "");
$entries[] = buildDropdownEntryPrintArray(
$idPrefix . "_aktuell",
"ausgewähltes Semester",
"xml=studienerfolg.rdf.php&xsl=$xsl&ss=$studiensemester_kurzbz" . ($fa ? "&typ=finanzamt" : ""),
$uid
);
//all semester
$entries[] = buildDropdownEntryPrintArray(
$idPrefix . "_all",
"alle Semester",
"xml=studienerfolg.rdf.php&xsl=$xsl&ss=$studiensemester_kurzbz&all=true" . ($fa ? "&typ=finanzamt" : ""),
$uid
);
//sem from array
foreach ($semArray as $i => $sem) {
$entries[] = buildDropdownEntryPrintArray(
$idPrefix . ($i === 0 ? "_akt" : "_minus" . $i),
$sem,
"xml=studienerfolg.rdf.php&xsl=$xsl&ss=$sem" . ($fa ? "&typ=finanzamt" : ""),
$uid
);
}
$order = 0;
if ($lang === "de" && !$fa) $order = 75; // Studienerfolg
if ($lang === "en" && !$fa) $order = 76; // Studienerfolg Englisch
if ($lang === "de" && $fa) $order = 77; // Studienerfolg Finanzamt
if ($lang === "en" && $fa) $order = 78; // Studienerfolg Finanzamt Englisch
return [
"id" => $idPrefix,
"type" => "submenu",
"name" => "Studienerfolg " . ($fa ? " Finanzamt" : "") . ($lang === "de" ? "" : "Englisch") ,
"order" => $order,
"data" => $entries,
];
}
private function loadDropDownEntriesFinalExam($hasPermissionOutputformat, $stgTyp, $uid)
{
if ($stgTyp == 'b')
$postfix = 'Bakk';
else if ($stgTyp == 'm' || $stgTyp == 'd')
$postfix = 'Master';
else
return [];
$arrayFinalExam = [
'pruefungsprotokoll' => [
'de' => [
'Bakk' => 'PrProtBA',
'Master' => 'PrProtMA',
],
'en' => [
'Bakk' => 'PrProtBAEng',
'Master' => 'PrProtMAEng',
],
],
'pruefungszeugnis' => [
'de' => [
'Bakk' => 'Bakkzeugnis',
'Master' => 'Diplomzeugnis',
],
'en' => [
'Bakk' => 'BakkzeugnisEng',
'Master' => 'DiplomzeugnisEng',
],
],
'urkunde' => [
'de' => [
'Bakk' => 'Bakkurkunde',
'Master' => 'Diplomurkunde',
],
'en' => [
'Bakk' => 'BakkurkundeEng',
'Master' => 'DiplomurkundeEng',
],
],
];
$langLabels = [
"de" => "Deutsch",
"en" => "Englisch"
];
$docLabels = [
"pruefungsprotokoll" => "Prüfungsprotokoll",
"pruefungszeugnis" => "Zeugnis",
"urkunde" => "Urkunde"
];
$submenuData = [];
if ($hasPermissionOutputformat) {
foreach ($arrayFinalExam as $docType => $langs) {
foreach ($langs as $lang => $types) {
$xsl = $types[$postfix];
$idPrefix = $docType . "_" . $lang;
$baseName = $docLabels[$docType] . " " . $langLabels[$lang];
$baseUrl = "xml=abschlusspruefung.rdf.php&xsl={$xsl}";
//3 outputformates
foreach (["pdf", "odt", "docx"] as $format) {
$submenuData[] = buildDropdownEntryPrintArray(
$idPrefix . "_" . $format,
$baseName . " (" . strtoupper($format) . ")",
$baseUrl . "&output=" . $format,
$uid
);
}
}
}
}
else
{
foreach ($arrayFinalExam as $docType => $langs) {
foreach ($langs as $lang => $types) {
$xsl = $types[$postfix]; // Auswahl Bakk/Master für jeweilige Sprache
$id = $docType . "_" . $lang;
$name = $docLabels[$docType] . " " . $langLabels[$lang];
$url = "xml=abschlusspruefung.rdf.php&xsl=" . $xsl . "&output=pdf";
$submenuData[] = buildDropdownEntryPrintArray($id, $name, $url, $uid);
}
}
}
return [
"id" => "submenu_finalexam",
"type" => "submenu",
"name" => "Abschlussprüfung",
"data" => $submenuData,
"order" => null,
"order" => 80,
];
}
}
@@ -9,6 +9,8 @@ class Gruppen extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'add' => ['admin:rw', 'assistenz:rw'],
'search' => ['admin:r', 'assistenz:r'],
'getGruppen' => ['admin:r', 'assistenz:r'],
'deleteGruppe' => ['admin:rw', 'assistenz:rw'],
]);
@@ -18,7 +20,9 @@ class Gruppen extends FHCAPI_Controller
// Load language phrases
$this->loadPhrases([
'ui', 'gruppenmanagement'
'ui',
'gruppenmanagement',
'lehre'
]);
// Load models
@@ -26,15 +30,140 @@ class Gruppen extends FHCAPI_Controller
$this->load->model('organisation/Gruppe_model', 'GruppeModel');
}
public function add()
{
$this->load->library("form_validation");
$this->form_validation->set_rules(
'gruppe_kurzbz',
$this->p->t('gruppenmanagement', 'gruppe'),
'required|is_in_db[organisation/Gruppe_model]',
[
'required' => $this->p->t('ui', 'error_fieldRequired'),
'is_in_db' => $this->p->t('ui', 'error_fieldNotFound')
]
);
$this->form_validation->set_rules(
'uid',
$this->p->t('ui', 'student_uid'),
'required|is_in_db[crm/Student_model:student_uid]',
[
'required' => $this->p->t('ui', 'error_fieldRequired'),
'is_in_db' => $this->p->t('ui', 'error_fieldNotFound')
]
);
$this->form_validation->set_rules(
'studiensemester_kurzbz',
$this->p->t('lehre', 'studiensemester'),
'required|is_in_db[organisation/Studiensemester_model]',
[
'required' => $this->p->t('ui', 'error_fieldRequired'),
'is_in_db' => $this->p->t('ui', 'error_fieldNotFound')
]
);
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$gruppe_kurzbz = $this->input->post('gruppe_kurzbz');
$uid = $this->input->post('uid');
$studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz');
$result = $this->BenutzergruppeModel->load([
$gruppe_kurzbz,
$uid
]);
$benutzergruppe = $this->getDataOrTerminateWithError($result);
if ($benutzergruppe) {
$this->terminateWithError(
$this->p->t('gruppenmanagement', 'error_alreadyInGroup', [
'uid' => $uid,
'studiensemester_kurzbz' => current($benutzergruppe)->studiensemester_kurzbz
]),
self::ERROR_TYPE_GENERAL
);
}
$result = $this->BenutzergruppeModel->insert([
'uid' => $uid,
'gruppe_kurzbz' => $gruppe_kurzbz,
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'insertamum' => date('c'),
'insertvon' => getAuthUID()
]);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess();
}
public function search()
{
$query = $this->input->post('query');
if (!$query)
$this->terminateWithSuccess([]);
// add query to where clause
$query = strtoupper($query);
$query = $this->GruppeModel->db->escape_like_str($query);
$query = '%' . str_replace(' ', '%', $query) . '%';
$this->GruppeModel->db->group_start();
$this->GruppeModel->db->or_like('UPPER(gruppe_kurzbz)', $query, 'none', false);
$this->GruppeModel->db->or_like('UPPER(bezeichnung)', $query, 'none', false);
$this->GruppeModel->db->or_like('UPPER(beschreibung)', $query, 'none', false);
$this->GruppeModel->db->group_end();
// add stg sorting 1
$studiengang_kz = $this->input->post('studiengang_kz');
$sort_stg = $studiengang_kz ? "WHEN studiengang_kz = " . $this->GruppeModel->escape($studiengang_kz) . " THEN 0" : "";
// add stg sorting 2
$studiengang_kzs = [];
$result = $this->permissionlib->getSTG_isEntitledFor('admin');
if ($result)
$studiengang_kzs = array_merge($studiengang_kzs, $result);
$result = $this->permissionlib->getSTG_isEntitledFor('assistenz');
if ($result)
$studiengang_kzs = array_merge($studiengang_kzs, $result);
// selects
$this->GruppeModel->addSelect("*");
$this->GruppeModel->addSelect("CASE
" . $sort_stg . "
WHEN studiengang_kz IN (" . implode(",", $this->GruppeModel->db->escape($studiengang_kzs)) . ")
THEN 1
ELSE 2
END AS sort_stg");
// ordering
$this->GruppeModel->addOrder("sort_stg");
$this->GruppeModel->addOrder("sort");
$this->GruppeModel->addOrder("gruppe_kurzbz");
// default where clause & execute
$result = $this->GruppeModel->loadWhere([
'lehre' => true,
'sichtbar' => true,
'aktiv' => true,
'direktinskription' => false
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getGruppen($student_uid)
{
$this->BenutzergruppeModel ->addSelect('gruppe_kurzbz');
$this->BenutzergruppeModel ->addSelect('bezeichnung');
$this->BenutzergruppeModel ->addSelect('generiert');
$this->BenutzergruppeModel ->addSelect('uid');
$this->BenutzergruppeModel ->addSelect('studiensemester_kurzbz');
$this->BenutzergruppeModel ->addJoin('public.tbl_gruppe', 'gruppe_kurzbz');
$this->BenutzergruppeModel-> addOrder('bezeichnung', 'ASC');
$this->BenutzergruppeModel->addSelect('gruppe_kurzbz');
$this->BenutzergruppeModel->addSelect('bezeichnung');
$this->BenutzergruppeModel->addSelect('generiert');
$this->BenutzergruppeModel->addSelect('uid');
$this->BenutzergruppeModel->addSelect('studiensemester_kurzbz');
$this->BenutzergruppeModel->addJoin('public.tbl_gruppe', 'gruppe_kurzbz');
$this->BenutzergruppeModel->addOrder('bezeichnung', 'ASC');
$result = $this->BenutzergruppeModel->loadWhere(
array(
@@ -49,29 +178,48 @@ class Gruppen extends FHCAPI_Controller
public function deleteGruppe()
{
$student_uid = $this->input->post('id');
$this->load->library("form_validation");
$this->form_validation->set_rules(
'uid',
$this->p->t('person', 'UID'),
'required',
[
'required' => $this->p->t('ui', 'error_fieldRequired')
]
);
$this->form_validation->set_rules(
'gruppe_kurzbz',
$this->p->t('gruppenmanagement', 'gruppe'),
'required',
[
'required' => $this->p->t('ui', 'error_fieldRequired')
]
);
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$uid = $this->input->post('uid');
$gruppe_kurzbz = $this->input->post('gruppe_kurzbz');
//Validate if automatic group generation
$result = $this->GruppeModel-> loadWhere(
array(
'gruppe_kurzbz' => $gruppe_kurzbz
)
);
// Validate if automatic group generation
$result = $this->GruppeModel->loadWhere([
'gruppe_kurzbz' => $gruppe_kurzbz
]);
$data = $this->getDataOrTerminateWithError($result);
$generation = current($data);
if($generation->generiert)
if ($generation->generiert)
{
$this->terminateWithError($this->p->t('gruppenmanagement', 'error_deleteGeneratedGroups'), self::ERROR_TYPE_GENERAL);
}
$result = $this->BenutzergruppeModel->delete(
array(
'gruppe_kurzbz' => $gruppe_kurzbz,
'uid' => $student_uid
)
);
$result = $this->BenutzergruppeModel->delete([
'gruppe_kurzbz' => $gruppe_kurzbz,
'uid' => $uid
]);
$data = $this->getDataOrTerminateWithError($result);
@@ -52,6 +52,7 @@ class Kontakt extends FHCAPI_Controller
// Extra Permissionchecks
$permsMa = [];
$permsStud = [];
$permsDefault = null;
switch ($this->router->method) {
case 'getBankverbindung':
case 'loadBankverbindung':
@@ -68,7 +69,7 @@ class Kontakt extends FHCAPI_Controller
case 'getKontakte':
case 'loadAddress':
case 'loadContact':
$permsMa = $permsStud = ['admin:r', 'assistenz:r'];
$permsMa = $permsStud = $permsDefault = ['admin:r', 'assistenz:r'];
break;
case 'addNewAddress':
case 'addNewContact':
@@ -76,7 +77,7 @@ class Kontakt extends FHCAPI_Controller
case 'updateContact':
case 'deleteAddress':
case 'deleteContact':
$permsMa = $permsStud = ['admin:rw', 'assistenz:rw'];
$permsMa = $permsStud = $permsDefault = ['admin:rw', 'assistenz:rw'];
break;
}
if ($this->router->method == 'getAdressen'
@@ -91,7 +92,7 @@ class Kontakt extends FHCAPI_Controller
if (is_null($person_id) || !ctype_digit((string)$person_id))
$this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->checkPermissionsForPerson($person_id, $permsMa, $permsStud);
$this->checkPermissionsForPerson($person_id, $permsMa, $permsStud, $permsDefault);
} elseif ($this->router->method == 'loadAddress'
|| $this->router->method == 'loadContact'
|| $this->router->method == 'loadBankverbindung'
@@ -135,7 +136,7 @@ class Kontakt extends FHCAPI_Controller
$person_id = current($data)->person_id;
$this->checkPermissionsForPerson($person_id, $permsMa, $permsStud);
$this->checkPermissionsForPerson($person_id, $permsMa, $permsStud, $permsDefault);
}
}
public function getAdressen($person_id)
@@ -352,7 +352,7 @@ class Konto extends FHCAPI_Controller
continue;
}
$result = $this->KontoModel->insert([
'person_id' => $buchung['person_id'],
'studiengang_kz' => $buchung['studiengang_kz'],
@@ -361,7 +361,7 @@ class Konto extends FHCAPI_Controller
'buchungstyp_kurzbz' => $buchung['buchungstyp_kurzbz'],
'credit_points' => $buchung['credit_points'],
'zahlungsreferenz' => $buchung['zahlungsreferenz'],
'betrag' => $betrag,
'betrag' => number_format($betrag, 2, '.', ''),
'buchungsdatum' => $buchungsdatum,
'mahnspanne' => '0',
'buchungsnr_verweis' => $buchung['buchungsnr'],
@@ -0,0 +1,63 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Lehrverband extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'hasOrgforms' => ['admin:r', 'assistenz:r'],
'getTree' => ['admin:r', 'assistenz:r'],
'getSpecialgroups' => ['admin:r', 'assistenz:r']
]);
}
public function hasOrgforms($studiengang_kz)
{
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$result = $this->StudiengangModel->load($studiengang_kz);
$data = $this->getDataOrTerminateWithError($result);
if ($data) {
$data = current($data)->mischform;
}
$this->terminateWithSuccess($data);
}
public function getTree($studiengang_kz)
{
$this->load->model('organisation/Lehrverband_model', 'LehrverbandModel');
$result = $this->LehrverbandModel->loadWhere([
'studiengang_kz' => $studiengang_kz,
'aktiv' => true
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getSpecialgroups($studiengang_kz)
{
$this->load->model('organisation/Gruppe_model', 'GruppeModel');
$where = [
'studiengang_kz' => $studiengang_kz,
'lehre' => true,
'sichtbar' => true,
'aktiv' => true,
'direktinskription' => false
];
$result = $this->GruppeModel->loadWhere($where);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -0,0 +1,368 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \DateTime as DateTime;
class Projektarbeit extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getProjektarbeit' => ['admin:r', 'assistenz:r'],
'loadProjektarbeit' => ['admin:r', 'assistenz:r'],
'insertProjektarbeit' => ['admin:rw', 'assistenz:rw'],
'updateProjektarbeit' => ['admin:rw', 'assistenz:rw'],
'deleteProjektarbeit' => ['admin:rw', 'assistenz:rw'],
'getTypenProjektarbeit' => ['admin:r', 'assistenz:r'],
'getFirmen' => ['admin:r', 'assistenz:r'],
'getLehrveranstaltungen' => ['admin:r', 'assistenz:r'],
'getNoten' => ['admin:r', 'assistenz:r']
]);
// Load Libraries
$this->load->library('form_validation');
// Load language phrases
$this->loadPhrases([
'ui',
'person',
'projektarbeit'
]);
// Load models
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
$this->load->model('education/Projekttyp_model', 'ProjekttypModel');
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$this->load->model('ressource/Firma_model', 'FirmaModel');
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('education/Note_model', 'NoteModel');
$this->load->model('education/Projektbetreuer_model', 'BetreuerModel');
// load libraries
$this->load->library('PermissionLib');
}
public function getProjektarbeit()
{
$student_uid = $this->input->get('uid');
if (!isset($student_uid)) $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL);
$result = $this->ProjektarbeitModel->getProjektarbeit($student_uid);
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
if (!hasData($result)) $this->terminateWithSuccess([]);
$projektarbeiten = getData($result);
foreach ($projektarbeiten as $projektarbeit)
{
$projektarbeit_id = $projektarbeit->projektarbeit_id;
$abgabeRes = $this->PaabgabeModel->getEndabgabe($projektarbeit_id);
if (isError($abgabeRes)) $this->terminateWithError(getError($abgabeRes), self::ERROR_TYPE_GENERAL);
if (hasData($abgabeRes))
{
$paabgabe = getData($abgabeRes)[0];
$projektarbeit->abgabedatum = $paabgabe->abgabedatum;
}
}
$this->terminateWithSuccess($projektarbeiten);
}
public function loadProjektarbeit()
{
$projektarbeit_id = $this->input->get('projektarbeit_id');
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id)) return $this->terminateWithError('Projektarbeit Id missing', self::ERROR_TYPE_GENERAL);
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
);
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id');
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id');
$this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT');
$result = $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
public function insertProjektarbeit()
{
$student_uid = $this->input->post('uid');
if (!$student_uid) return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL);
if (!$this->_hasBerechtigungForStudent($student_uid))
return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]);
$formData = $this->input->post('formData');
if ($this->_validate($formData) == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$projektarbeit = $this->_getProjektarbeitArr($formData);
$result = $this->ProjektarbeitModel->insert(
array_merge($projektarbeit, ['insertamum' => date('c'), 'insertvon' => getAuthUID(), 'student_uid' => $student_uid])
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function updateProjektarbeit()
{
$projektarbeit_id = $this->input->post('projektarbeit_id');
if (!$projektarbeit_id || !is_numeric($projektarbeit_id))
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Projektarbeit ID']), self::ERROR_TYPE_GENERAL);
if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id))
return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]);
$formData = $this->input->post('formData');
if ($this->_validate($formData) == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$projektarbeit = $this->_getProjektarbeitArr($formData);
$result = $this->ProjektarbeitModel->update(
$projektarbeit_id,
array_merge($projektarbeit, ['updateamum' => date('c'), 'updatevon' => getAuthUID()])
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function deleteProjektarbeit()
{
$projektarbeit_id = $this->input->post('projektarbeit_id');
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id))
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Projektarbeit ID'], self::ERROR_TYPE_GENERAL));
if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id))
return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]);
$validate = $this->_validateDelete($projektarbeit_id);
if (isError($validate)) return $this->terminateWithError(getError($validate), self::ERROR_TYPE_GENERAL);
$result = $this->ProjektarbeitModel->delete(
['projektarbeit_id' => $projektarbeit_id]
);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
if (!hasData($result))
{
$this->outputJson($result);
}
return $this->terminateWithSuccess(current(getData($result)) ? : null);
}
public function getTypenProjektarbeit()
{
$result = $this->ProjekttypModel->loadWhere(['aktiv' => true]);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function getFirmen()
{
$searchString = $this->input->get('searchString');
if (!isset($searchString))
$this->terminateWithError($this->p->t('ui', 'error_fieldRequired', ['field' => 'Search term']), self::ERROR_TYPE_GENERAL);
$result = $this->FirmaModel->searchFirmen($searchString, $aktiv = true);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function getLehrveranstaltungen()
{
$student_uid = $this->input->get('student_uid');
$studiengang_kz = $this->input->get('studiengang_kz');
$studiensemester_kurzbz = $this->input->get('studiensemester_kurzbz');
$additional_lehrveranstaltung_id = $this->input->get('additional_lehrveranstaltung_id');
if (!isset($student_uid)) $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL);
if (!isset($studiensemester_kurzbz)) $this->terminateWithError('Studiensemster missing', self::ERROR_TYPE_GENERAL);
$lvsResult = $this->LehrveranstaltungModel->getLvsForProjektarbeit($student_uid, $studiengang_kz, $additional_lehrveranstaltung_id);
if (isError($lvsResult)) return $this->terminateWithError($lvsResult, self::ERROR_TYPE_GENERAL);
$lvs = hasData($lvsResult) ? getData($lvsResult) : [];
foreach ($lvs as $lv)
{
$lehreinheiten = $this->LehreinheitModel->getLesForLv(
$lv->lehrveranstaltung_id, $studiensemester_kurzbz
);
foreach ($lehreinheiten as $lehreinheit)
{
if (!isEmptyArray($lehreinheit->lektoren))
{
$this->MitarbeiterModel->addSelect('kurzbz');
$this->MitarbeiterModel->db->where_in('tbl_mitarbeiter.mitarbeiter_uid', $lehreinheit->lektoren);
$maResult = $this->MitarbeiterModel->load();
if (isError($maResult)) return $this->terminateWithError($lvsResult, self::ERROR_TYPE_GENERAL);
$lehreinheit->lektoren = array_column(getData($maResult), 'kurzbz');
}
}
$lv->lehreinheiten = $lehreinheiten;
}
return $this->terminateWithSuccess($lvs);
}
public function getNoten()
{
$result = $this->NoteModel->load();
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
/**
*
* @param
* @return object success or error
*/
private function _validate($formData)
{
$this->form_validation->set_data($formData);
$this->form_validation->set_rules('titel', 'Titel', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Titel'])
]);
$this->form_validation->set_rules('projekttyp_kurzbz', 'Projekttyp', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Projekttyp'])
]);
$this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'required|is_natural', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']),
'is_natural' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Lehreinheit'])
]);
$this->form_validation->set_rules('beginn', 'Beginn', 'is_valid_date', [
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'Beginn'])
]);
$this->form_validation->set_rules('ende', 'Ende', 'is_valid_date', [
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'Ende'])
]);
$this->form_validation->set_rules('gesperrtbis', 'Ende', 'is_valid_date', [
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'Gesperrt bis'])
]);
return $this->form_validation->run();
}
/**
*
* @param
* @return object success or error
*/
private function _getProjektarbeitArr($formData)
{
return [
'titel' => $formData['titel'],
'titel_english' => $formData['titel_english'] ?? null,
'themenbereich' => $formData['themenbereich'] ?? null,
'projekttyp_kurzbz' => $formData['projekttyp_kurzbz'],
'firma_id' => $formData['firma_id'] ?? null,
'lehreinheit_id' => $formData['lehreinheit_id'],
'beginn' => isset($formData['beginn']) && !isEmptyString($formData['beginn']) ? $formData['beginn'] : null,
'ende' => isset($formData['ende']) && !isEmptyString($formData['ende']) ? $formData['ende'] : null,
'note' => $formData['note'] ?? null,
'final' => $formData['final'] ?? null,
'freigegeben' => $formData['freigegeben'] ?? null,
'anmerkung' => $formData['anmerkung'] ?? null,
'gesperrtbis' => isset($formData['gesperrtbis']) && !isEmptyString($formData['gesperrtbis']) ? $formData['gesperrtbis'] : null
];
}
/**
*
* @param
* @return object success or error
*/
private function _validateDelete($projektarbeit_id)
{
$this->BetreuerModel->addSelect('1');
$result = $this->BetreuerModel->loadWhere(['projektarbeit_id' => $projektarbeit_id]);
if (isError($result)) return $result;
if (hasData($result)) return error($this->p->t('projektarbeit', 'error_betreuerNichtGeloescht'));
$this->PaabgabeModel->addSelect('1');
$result = $this->PaabgabeModel->loadWhere(['projektarbeit_id' => $projektarbeit_id]);
if (isError($result)) return $result;
if (hasData($result)) return error($this->p->t('projektarbeit', 'error_paabgabeNichtGeloescht'));
return success();
}
private function _hasBerechtigungForStudent($student_uid)
{
if (!$student_uid)
return false;
$this->load->model('crm/Student_model', 'StudentModel');
$this->StudentModel->addSelect('studiengang_kz');
$result = $this->StudentModel->load([$student_uid]);
if (isError($result) || !hasData($result))
return false;
$studiengang_kz = getData($result)[0]->studiengang_kz;
if ($this->permissionlib->isBerechtigt('admin', 'suid', $studiengang_kz))
return true;
if ($this->permissionlib->isBerechtigt('assistenz', 'suid', $studiengang_kz))
return true;
return false;
}
}
@@ -0,0 +1,341 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \DateTime as DateTime;
use CI3_Events as Events;
class Projektbetreuer extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getProjektbetreuer' => ['admin:r', 'assistenz:r'],
'saveProjektbetreuer' => ['admin:rw', 'assistenz:rw'],
'deleteProjektbetreuer' => ['admin:rw', 'assistenz:rw'],
'getBetreuerarten' => ['admin:r', 'assistenz:r'],
'getNoten' => ['admin:r', 'assistenz:r'],
'getDefaultStundensaetze' => ['admin:r', 'assistenz:r'],
'getProjektbetreuerBySearchQuery' => ['admin:r', 'assistenz:r'],
'getPerson' => ['admin:r', 'assistenz:r'],
'validateProjektbetreuer' => ['admin:r', 'assistenz:r']
]);
// Load Libraries
$this->load->library('form_validation');
// Load language phrases
$this->loadPhrases([
'ui',
'person',
'projektarbeit'
]);
// Load models
$this->load->model('education/Projektbetreuer_model', 'ProjektbetreuerModel');
$this->load->model('education/Betreuerart_model', 'BetreuerartModel');
$this->load->model('ressource/Stundensatz_model', 'StundensatzModel');
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
$this->load->model('education/Note_model', 'NoteModel');
$this->load->model('person/Person_model', 'PersonModel');
// load libraries
$this->load->library('PermissionLib');
}
public function getProjektbetreuer()
{
$projektarbeit_id = $this->input->get('projektarbeit_id');
if (!isset($projektarbeit_id))
$this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Projektarbeit ID']), self::ERROR_TYPE_GENERAL);
$this->ProjektbetreuerModel->addSelect(
'projektarbeit_id, person_id, nachname, vorname, note, punkte, round(stunden, 1) AS stunden,
stundensatz, betreuerart_kurzbz, vertrag_id, titelpre, titelpost'
);
$this->ProjektbetreuerModel->addSelect("CASE
WHEN EXISTS
(SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid) WHERE person_id=pers.person_id)
THEN 'Mitarbeiter'
WHEN EXISTS
(SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_student ON(uid=student_uid) WHERE person_id=pers.person_id)
THEN 'Student'
ELSE 'Person'
END AS status");
$this->ProjektbetreuerModel->addJoin('public.tbl_person pers', 'person_id');
$result = $this->ProjektbetreuerModel->loadWhere(['projektarbeit_id' => $projektarbeit_id]);
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
if (!hasData($result)) $this->terminateWithSuccess([]);
$projektbetreuer = getData($result);
//~ foreach ($projektbetreuer as $projektarbeit)
//~ {
//~ $projektarbeit_id = $projektarbeit->projektarbeit_id;
//~ $abgabeRes = $this->PaabgabeModel->getEndabgabe($projektarbeit_id);
//~ if (isError($abgabeRes)) $this->terminateWithError(getError($abgabeRes), self::ERROR_TYPE_GENERAL);
//~ if (hasData($abgabeRes))
//~ {
//~ $paabgabe = getData($abgabeRes)[0];
//~ $projektarbeit->abgabedatum = $paabgabe->abgabedatum;
//~ }
//~ }
foreach ($projektbetreuer as $pb)
{
$downloadLink = null;
Events::trigger(
'projektbeurteilung_download_link',
$pb->projektarbeit_id,
$pb->betreuerart_kurzbz,
$pb->person_id,
function ($value) use (&$downloadLink) {
$downloadLink = $value;
}
);
$pb->beurteilungDownloadLink = $downloadLink;
}
$this->terminateWithSuccess($this->_addFullNameToBetreuer($projektbetreuer));
}
public function saveProjektbetreuer()
{
$projektarbeit_id = $this->input->post('projektarbeit_id');
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id))
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Projektarbeit ID']), self::ERROR_TYPE_GENERAL);
if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id))
return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]);
$projektbetreuer = $this->input->post('projektbetreuer');
if ($this->_validate($projektbetreuer) == false) $this->terminateWithValidationErrors($this->form_validation->error_array());
$result = null;
$betreuer = [
'projektarbeit_id' => $projektarbeit_id,
'person_id' => $projektbetreuer['person_id'],
'note' => $projektbetreuer['note'],
'stunden' => $projektbetreuer['stunden'],
'stundensatz' => $projektbetreuer['stundensatz'],
'betreuerart_kurzbz' => $projektbetreuer['betreuerart_kurzbz']
];
if (isset($projektbetreuer['person_id_old']) && isset($projektbetreuer['betreuerart_kurzbz_old']))
{
$result = $this->ProjektbetreuerModel->update(
[
'projektarbeit_id' => $projektarbeit_id,
'person_id' => $projektbetreuer['person_id_old'],
'betreuerart_kurzbz' => $projektbetreuer['betreuerart_kurzbz_old']
],
array_merge($betreuer, ['updateamum' => date('c'), 'updatevon' => getAuthUID()])
);
}
else
{
$result = $this->ProjektbetreuerModel->insert(
array_merge($betreuer, ['insertamum' => date('c'), 'insertvon' => getAuthUID()])
);
}
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function deleteProjektbetreuer()
{
$projektarbeit_id = $this->input->post('projektarbeit_id');
$person_id = $this->input->post('person_id');
$betreuerart_kurzbz = $this->input->post('betreuerart_kurzbz');
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id))
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> $this->p->t('projektarbeit', 'projektarbeit').' ID'], self::ERROR_TYPE_GENERAL));
if (!isset($person_id) || !is_numeric($person_id))
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Person ID'], self::ERROR_TYPE_GENERAL));
if (!isset($betreuerart_kurzbz))
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> $this->p->t('projektarbeit', 'betreuerart')], self::ERROR_TYPE_GENERAL));
if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id))
return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]);
$validate = $this->_validateDelete($projektarbeit_id, $person_id);
if (isError($validate)) return $this->terminateWithError(getError($validate), self::ERROR_TYPE_GENERAL);
$result = $this->ProjektbetreuerModel->delete(
['projektarbeit_id' => $projektarbeit_id, 'person_id' => $person_id, 'betreuerart_kurzbz' => $betreuerart_kurzbz]
);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
if (!hasData($result))
{
$this->outputJson($result);
}
return $this->terminateWithSuccess(current(getData($result)) ? : null);
}
public function getBetreuerarten()
{
$result = $this->BetreuerartModel->loadWhere(['aktiv' => true]);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function getNoten()
{
$result = $this->NoteModel->load();
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function getDefaultStundensaetze()
{
$person_id = $this->input->get('person_id');
$studiensemester_kurzbz = $this->input->get('studiensemester_kurzbz');
$result = $this->StundensatzModel->getStundensatzForMitarbeiter($person_id, $studiensemester_kurzbz);
return $this->terminateWithSuccess($result);
}
public function getProjektbetreuerBySearchQuery()
{
$searchString = $this->input->get('searchString');
if (!isset($searchString))
$this->terminateWithError($this->p->t('ui', 'error_fieldRequired', ['field' => 'Search term']), self::ERROR_TYPE_GENERAL);
$result = $this->PersonModel->searchPerson($searchString);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? $this->_addFullNameToBetreuer(getData($result)) : []);
}
public function getPerson()
{
$person_id = $this->input->get('person_id');
if (!isset($person_id))
$this->terminateWithError($this->p->t('ui', 'error_fieldRequired', ['field' => 'Person']), self::ERROR_TYPE_GENERAL);
$this->PersonModel->addSelect("CASE
WHEN EXISTS
(SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid) WHERE person_id=tbl_person.person_id)
THEN 'Mitarbeiter'
WHEN EXISTS
(SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_student ON(uid=student_uid) WHERE person_id=tbl_person.person_id)
THEN 'Student'
ELSE 'Person'
END AS status");
$result = $this->PersonModel->addSelect('titelpre, titelpost, vorname, nachname, person_id');
$result = $this->PersonModel->load($person_id);
if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
return $this->terminateWithSuccess(hasData($result) ? $this->_addFullNameToBetreuer(getData($result))[0] : []);
}
/**
*
* @param
* @return object success or error
*/
public function validateProjektbetreuer()
{
$projektbetreuerArr = $this->input->post('projektbetreuer');
if (!is_array($projektbetreuerArr)) $projektbetreuerArr = [$projektbetreuerArr];
foreach ($projektbetreuerArr as $pb)
{
if ($this->_validate($pb) == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
}
$this->terminateWithSuccess([]);
}
/**
*
* @param
* @return object success or error
*/
private function _validate($formData)
{
$this->form_validation->set_data($formData);
$this->form_validation->set_rules('betreuerart_kurzbz', 'Betreuerart', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('projektarbeit', 'betreuerart')])
]);
$this->form_validation->set_rules('person_id', 'Person', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('projektarbeit', 'betreuer')])
]);
$this->form_validation->set_rules('stunden', 'Stunden', 'numeric', [
'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => $this->p->t('projektarbeit', 'stunden')])
]);
$this->form_validation->set_rules('stundensatz', 'Stundensatz', 'numeric', [
'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => $this->p->t('projektarbeit', 'stundensatz')])
]);
return $this->form_validation->run();
}
/**
*
* @param
* @return object success or error
*/
private function _validateDelete($projektarbeit_id, $person_id)
{
$this->ProjektbetreuerModel->addSelect('vertrag_id');
$result = $this->ProjektbetreuerModel->loadWhere(['projektarbeit_id' => $projektarbeit_id, 'person_id' => $person_id]);
if (isError($result)) return $result;
if (hasData($result) && getData($result)[0]->vertrag_id != null) return error($this->p->t('projektarbeit', 'error_betreuerHatVertrag'));
return success();
}
/**
*
* @param
* @return object success or error
*/
private function _addFullNameToBetreuer($betreuerArr)
{
foreach ($betreuerArr as $betreuer)
{
$betreuer->name = ($betreuer->titelpre ? $betreuer->titelpre . ' ' : '') .
$betreuer->nachname . ' ' . $betreuer->vorname . ($betreuer->titelpost ? ' ' . $betreuer->titelpre : '').
' (' . $betreuer->status . ')';
}
return $betreuerArr;
}
}
@@ -114,9 +114,8 @@ class Status extends FHCAPI_Controller
$this->load->model('codex/Bismeldestichtag_model', 'BismeldestichtagModel');
$result = $this->BismeldestichtagModel->getLastReachedMeldestichtag();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
$this->terminateWithSuccess(hasData($result) ? getData($result) : array());
}
public function isLastStatus($prestudent_id)
@@ -296,7 +295,7 @@ class Status extends FHCAPI_Controller
}],
//Check if Rolle already exists
['rolle_doesnt_exist', function () use ($prestudent_id, $status_kurzbz, $studiensemester_kurzbz, $ausbildungssemester) {
if (!$status_kurzbz || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->PrestudentstatusModel->load([$ausbildungssemester, $studiensemester_kurzbz, $status_kurzbz, $prestudent_id]);
@@ -903,7 +902,7 @@ class Status extends FHCAPI_Controller
$this->form_validation->set_rules('_default', '', [
['rolle_doesnt_exist', function () use ($prestudent_id, $status_kurzbz, $studiensemester_kurzbz, $ausbildungssemester) {
if (!$status_kurzbz || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->PrestudentstatusModel->load([$ausbildungssemester, $studiensemester_kurzbz, $status_kurzbz, $prestudent_id]);
@@ -920,7 +919,7 @@ class Status extends FHCAPI_Controller
) {
if ($isBerechtigtNoStudstatusCheck)
return true; // Skip if access right says so
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->prestudentstatuschecklib->checkStatusHistoryTimesequence(
@@ -945,7 +944,7 @@ class Status extends FHCAPI_Controller
) {
if ($isBerechtigtNoStudstatusCheck)
return true; // Skip if access right says so
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->prestudentstatuschecklib->checkStatusHistoryLaststatus(
@@ -970,7 +969,7 @@ class Status extends FHCAPI_Controller
) {
if ($isBerechtigtNoStudstatusCheck)
return true; // Skip if access right says so
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->prestudentstatuschecklib->checkStatusHistoryUnterbrechersemester(
@@ -995,7 +994,7 @@ class Status extends FHCAPI_Controller
) {
if ($isBerechtigtNoStudstatusCheck)
return true; // Skip if access right says so
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->prestudentstatuschecklib->checkStatusHistoryAbbrechersemester(
@@ -1020,7 +1019,7 @@ class Status extends FHCAPI_Controller
) {
if ($isBerechtigtNoStudstatusCheck)
return true; // Skip if access right says so
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !$ausbildungssemester)
if (!$status_kurzbz || !$datum || !$studiensemester_kurzbz || !isset($ausbildungssemester) || $ausbildungssemester === '')
return true; // Error will be handled by the required statements above
$result = $this->prestudentstatuschecklib->checkStatusHistoryDiplomant(
@@ -36,6 +36,7 @@ class Student extends FHCAPI_Controller
parent::__construct([
'get' => ['admin:r', 'assistenz:r'],
'save' => ['admin:rw', 'assistenz:rw'],
'saveStudent' => ['admin:rw', 'assistenz:rw'],
'check' => ['admin:rw', 'assistenz:rw'],
'add' => ['admin:rw', 'assistenz:rw'] // TODO(chris): extra permissions
]);
@@ -55,7 +56,7 @@ class Student extends FHCAPI_Controller
// Load language phrases
$this->loadPhrases([
'ui', 'lehre'
'ui', 'lehre', 'person'
]);
}
@@ -424,6 +425,31 @@ class Student extends FHCAPI_Controller
), ''));
}
/**
* Saves data to a prestudent using their student_uid
*
* @param string $student_uid
* @param string $studiensemester_kurzbz
* @return void
*/
public function saveStudent($student_uid, $studiensemester_kurzbz)
{
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->load([$student_uid]);
$data = $this->getDataOrTerminateWithError($result);
if (!$data)
show_404(); // No Student with that ID
$student = current($data);
$this->checkPermissionsForPrestudent($student->prestudent_id, ['admin:rw', 'assistenz:rw']);
return $this->save($student->prestudent_id, $studiensemester_kurzbz);
}
public function check()
{
$this->load->library('form_validation');
@@ -465,7 +491,6 @@ class Student extends FHCAPI_Controller
if (!$this->input->post('person_id')) {
if (!isset($_POST['address']) || !is_array($_POST['address']))
$_POST['address'] = [];
$_POST['address']['func'] = 1;
}
if ($this->input->post('incoming')) {
$_POST['ausbildungssemester'] = 0;
@@ -474,31 +499,37 @@ class Student extends FHCAPI_Controller
$this->load->library('form_validation');
$this->form_validation->set_rules('nachname', 'Nachname', 'callback_requiredIfNotPersonId', [
'requiredIfNotPersonId' => $this->p->t('ui', 'error_required')
'requiredIfNotPersonId' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('person', 'nachname')])
]);
$this->form_validation->set_rules('geschlecht', 'Geschlecht', 'callback_requiredIfNotPersonId', [
'requiredIfNotPersonId' => $this->p->t('ui', 'error_required')
'requiredIfNotPersonId' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('person', 'geschlecht')])
]);
$this->form_validation->set_rules('gebdatum', 'Geburtsdatum', 'callback_isValidDate', [
$this->form_validation->set_rules('gebdatum', 'Geburtsdatum', ['isValidDate', function($value) { return isValidDate($value); }], [
'isValidDate' => $this->p->t('ui', 'error_invalid_date')
]);
$this->form_validation->set_rules('address[func]', 'Address', 'required|integer|less_than[2]|greater_than[-2]');
$this->form_validation->set_rules('address[plz]', 'PLZ', 'callback_requiredIfAddressFunc', [
'requiredIfAddressFunc' => $this->p->t('ui', 'error_required')
'requiredIfAddressFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('person', 'plz')])
]);
$this->form_validation->set_rules('address[gemeinde]', 'Gemeinde', 'callback_requiredIfAddressFunc', [
'requiredIfAddressFunc' => $this->p->t('ui', 'error_required')
'requiredIfAddressFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('person', 'gemeinde')])
]);
$this->form_validation->set_rules('address[ort]', 'Ort', 'callback_requiredIfAddressFunc', [
'requiredIfAddressFunc' => $this->p->t('ui', 'error_required')
'requiredIfAddressFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('person', 'ort')])
]);
$this->form_validation->set_rules('address[address]', 'Adresse', 'callback_requiredIfAddressFunc', [
'requiredIfAddressFunc' => $this->p->t('ui', 'error_required')
'requiredIfAddressFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('person', 'adresse')])
]);
$this->form_validation->set_rules('email', 'E-Mail', 'valid_email');
$this->form_validation->set_rules('studiengang_kz', 'Studiengang', 'required');
$this->form_validation->set_rules('studiensemester_kurzbz', 'Studiensemester', 'required');
$this->form_validation->set_rules('ausbildungssemester', 'Ausbildungssemester', 'required|integer|less_than[9]|greater_than[-1]');
$this->form_validation->set_rules('studiengang_kz', 'Studiengang', 'callback_requiredIfStudentFunc', [
'requiredIfStudentFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'studiengang')])
]);
$this->form_validation->set_rules('studiensemester_kurzbz', 'Studiensemester', 'callback_requiredIfStudentFunc', [
'requiredIfStudentFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'studiensemester')])
]);
$this->form_validation->set_rules('ausbildungssemester', 'Ausbildungssemester', 'callback_requiredIfStudentFunc|integer|less_than[9]|greater_than[-1]', [
'requiredIfStudentFunc' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'ausbildungssemester')])
]);
// TODO(chris): validate studienplan with studiengang, semester and orgform?
// TODO(chris): validate person_id, studiengang_kz, studiensemester_kurzbz, orgform_kurzbz, nation, gemeinde, ort, geschlecht?
@@ -518,7 +549,9 @@ class Student extends FHCAPI_Controller
if ($this->db->trans_status() === FALSE)
$this->terminateWithError('TODO(chris): TEXT', self::ERROR_TYPE_GENERAL);
$this->terminateWithSuccess($result);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
protected function addInteressent()
@@ -575,6 +608,8 @@ class Student extends FHCAPI_Controller
'zustelladresse' => true,
];
if ($anlegen < 0) { // Überschreiben
$this->AdresseModel->addSelect('adresse_id');
$this->AdresseModel->addJoin('public.tbl_adressentyp', 'typ = adressentyp_kurzbz');
$this->AdresseModel->addOrder('zustelladresse', 'DESC');
$this->AdresseModel->addOrder('sort');
$result = $this->AdresseModel->loadWhere([
@@ -631,70 +666,74 @@ class Student extends FHCAPI_Controller
}
}
// Prestudent anlegen
$data = [
'aufmerksamdurch_kurzbz' => 'k.A.',
'person_id' => $person_id,
'studiengang_kz' => $this->input->post('studiengang_kz'),
'ausbildungcode' => $this->input->post('letzteausbildung'),
'anmerkung' => $this->input->post('anmerkungen'),
'reihungstestangetreten' => false,
'bismelden' => true
];
$ausbildungsart = $this->input->post('ausbildungsart');
if ($ausbildungsart)
$data['anmerkung'] .= ' Ausbildungsart:' . $ausbildungsart;
// Incomings und ausserordentliche sind bei Meldung nicht förderrelevant
$incoming = $this->input->post('incoming');
if ($incoming || substr($data['studiengang_kz'], 0, 1) == '9')
$data['foerderrelevant'] = false;
// Wenn die Person schon im System erfasst ist, dann die ZGV des Datensatzes uebernehmen
$this->PrestudentModel->addOrder('zgvmas_code');
$this->PrestudentModel->addOrder('zgv_code', 'DESC');
$this->PrestudentModel->addLimit(1);
$result = $this->PrestudentModel->loadWhere([
'person_id' => $person_id
]);
$prestudent = $this->getDataOrTerminateWithError($result);
if ($prestudent) {
$prestudent = current($prestudent);
if ($prestudent->zgv_code) {
$data['zgv_code'] = $prestudent->zgv_code;
$data['zgvort'] = $prestudent->zgvort;
$data['zgvdatum'] = $prestudent->zgvdatum;
$personOnly = $anlegen = $this->input->post('personOnly');
$data['zgvmas_code'] = $prestudent->zgvmas_code;
$data['zgvmaort'] = $prestudent->zgvmaort;
$data['zgvmadatum'] = $prestudent->zgvmadatum;
if (!$personOnly)
{
// Prestudent anlegen
$data = [
'aufmerksamdurch_kurzbz' => 'k.A.',
'person_id' => $person_id,
'studiengang_kz' => $this->input->post('studiengang_kz'),
'ausbildungcode' => $this->input->post('letzteausbildung'),
'anmerkung' => $this->input->post('anmerkungen'),
'reihungstestangetreten' => false,
'bismelden' => true
];
$ausbildungsart = $this->input->post('ausbildungsart');
if ($ausbildungsart)
$data['anmerkung'] .= ' Ausbildungsart:' . $ausbildungsart;
// Incomings und ausserordentliche sind bei Meldung nicht förderrelevant
$incoming = $this->input->post('incoming');
if ($incoming || substr($data['studiengang_kz'], 0, 1) == '9')
$data['foerderrelevant'] = false;
// Wenn die Person schon im System erfasst ist, dann die ZGV des Datensatzes uebernehmen
$this->PrestudentModel->addOrder('zgvmas_code');
$this->PrestudentModel->addOrder('zgv_code', 'DESC');
$this->PrestudentModel->addLimit(1);
$result = $this->PrestudentModel->loadWhere([
'person_id' => $person_id
]);
$prestudent = $this->getDataOrTerminateWithError($result);
if ($prestudent) {
$prestudent = current($prestudent);
if ($prestudent->zgv_code) {
$data['zgv_code'] = $prestudent->zgv_code;
$data['zgvort'] = $prestudent->zgvort;
$data['zgvdatum'] = $prestudent->zgvdatum;
$data['zgvmas_code'] = $prestudent->zgvmas_code;
$data['zgvmaort'] = $prestudent->zgvmaort;
$data['zgvmadatum'] = $prestudent->zgvmadatum;
}
}
// Prestudent speichern
$result = $this->PrestudentModel->insert($data);
$prestudent_id = $this->getDataOrTerminateWithError($result);
// Prestudent Rolle Anlegen
$data = [
'prestudent_id' => $prestudent_id,
'status_kurzbz' => $incoming ? 'Incoming' : 'Interessent',
'studiensemester_kurzbz' => $this->input->post('studiensemester_kurzbz'),
'ausbildungssemester' => $this->input->post('ausbildungssemester') ?: 0,
'orgform_kurzbz' => $this->input->post('orgform_kurzbz') ?: null,
'studienplan_id' => $this->input->post('studienplan_id') ?: null,
'datum' => date('Y-m-d'),
'insertamum' => date('c'),
'insertvon' => getAuthUID()
];
$result = $this->PrestudentstatusModel->insert($data);
$this->getDataOrTerminateWithError($result);
if ($incoming) {
// TODO(chris): IMPLEMENT!
//Matrikelnummer und UID generieren
//Benutzerdatensatz anlegen
//Studentendatensatz anlegen
//StudentLehrverband anlegen
}
}
// Prestudent speichern
$result = $this->PrestudentModel->insert($data);
$prestudent_id = $this->getDataOrTerminateWithError($result);
// Prestudent Rolle Anlegen
$data = [
'prestudent_id' => $prestudent_id,
'status_kurzbz' => $incoming ? 'Incoming' : 'Interessent',
'studiensemester_kurzbz' => $this->input->post('studiensemester_kurzbz'),
'ausbildungssemester' => $this->input->post('ausbildungssemester') ?: 0,
'orgform_kurzbz' => $this->input->post('orgform_kurzbz') ?: null,
'studienplan_id' => $this->input->post('studienplan_id') ?: null,
'datum' => date('Y-m-d'),
'insertamum' => date('c'),
'insertvon' => getAuthUID()
];
$result = $this->PrestudentstatusModel->insert($data);
$this->getDataOrTerminateWithError($result);
if ($incoming) {
// TODO(chris): IMPLEMENT!
//Matrikelnummer und UID generieren
//Benutzerdatensatz anlegen
//Studentendatensatz anlegen
//StudentLehrverband anlegen
}
// TODO(chris): DEBUG
/*$result = $this->PrestudentModel->loadWhere([
'pestudent_id' => 1
@@ -703,7 +742,7 @@ class Student extends FHCAPI_Controller
return $result;
}*/
return success(true);
return success($person_id);
}
public function requiredIfNotPersonId($value)
@@ -715,7 +754,14 @@ class Student extends FHCAPI_Controller
public function requiredIfAddressFunc($value)
{
if (!$_POST['address']['func'])
if (!$_POST['address']['func'] || $_POST['address']['func'] == 0)
return true;
return !!$value;
}
public function requiredIfStudentFunc($value)
{
if ($_POST['personOnly'])
return true;
return !!$value;
}
@@ -44,14 +44,12 @@ class Students extends FHCAPI_Controller
}
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$this->load->library('PhrasesLib');
$this->loadPhrases(
array(
'lehre'
)
);
}
/**
@@ -75,7 +73,7 @@ class Students extends FHCAPI_Controller
* /(studiengang_kz)/(orgform)/prestudent/(studiensemester_kurzbz)/(filter) => getPrestudentsOrgform
* /(studiengang_kz)/(orgform)/prestudent/(studiensemester_kurzbz)/(filter)/(otherfilter) => getPrestudentsOrgform
*
* /(studiensemester_kurzbz)/(studiengang_kz)/(semester)/grp/(gruppe) => getStudentsSpezialguppe
* /(studiensemester_kurzbz)/(studiengang_kz)/(semester)/grp/(gruppe) => getStudentsSpezialgruppe
*
* /(studiensemester_kurzbz)/(studiengang_kz) => getStudents
* /(studiensemester_kurzbz)/(studiengang_kz)/(semester) => getStudents
@@ -101,39 +99,183 @@ class Students extends FHCAPI_Controller
}
/**
* @param string $studiensemester_kurzbz
*
* @return void
*/
public function getIncoming()
public function getIncoming($studiensemester_kurzbz)
{
$this->addMeta('ci_method', __FUNCTION__);
// TODO(chris): IMPLEMENT!
$this->terminateWithSuccess([]);
$this->addMeta('ci_params', [
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
$this->PrestudentModel->addJoin(
"(
SELECT prestudent_id
FROM public.tbl_prestudentstatus
WHERE status_kurzbz = 'Incoming'
AND studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
) test",
"prestudent_id"
);
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect("COALESCE(
v.semester::text,
CASE
WHEN pls.status_kurzbz IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent')
THEN pls.ausbildungssemester::text
ELSE ''::text
END
) AS semester", false);
$this->PrestudentModel->addSelect("COALESCE(v.verband::text, ''::text)");
$this->PrestudentModel->addSelect("COALESCE(v.gruppe::text, ''::text)");
$this->addSelectPrioRel();
$this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
/**
* @param string $studiensemester_kurzbz
*
* @return void
*/
public function getOutgoing()
public function getOutgoing($studiensemester_kurzbz)
{
$this->addMeta('ci_method', __FUNCTION__);
// TODO(chris): IMPLEMENT!
$this->terminateWithSuccess([]);
$this->addMeta('ci_params', [
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
$this->PrestudentModel->addJoin(
"(
SELECT prestudent_id
FROM bis.tbl_bisio bis
JOIN public.tbl_student USING (student_uid)
JOIN public.tbl_studiensemester stdsem ON (
(bis.von >= stdsem.start AND bis.von <= stdsem.ende)
OR
(bis.bis >= stdsem.start AND bis.bis <= stdsem.ende)
OR
(bis.von <= stdsem.start AND bis.bis >= stdsem.ende)
)
WHERE NOT EXISTS (
SELECT 1
FROM public.tbl_prestudentstatus
WHERE status_kurzbz = 'Incoming'
AND prestudent_id = tbl_student.prestudent_id
) AND stdsem.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
GROUP BY prestudent_id
) test",
"prestudent_id"
);
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect("COALESCE(
v.semester::text,
CASE
WHEN pls.status_kurzbz IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent')
THEN pls.ausbildungssemester::text
ELSE ''::text
END
) AS semester", false);
$this->PrestudentModel->addSelect("COALESCE(v.verband::text, ''::text)");
$this->PrestudentModel->addSelect("COALESCE(v.gruppe::text, ''::text)");
$this->addSelectPrioRel();
$this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
/**
* @param string $studiensemester_kurzbz
*
* @return void
*/
public function getGemeinsamestudien()
public function getGemeinsamestudien($studiensemester_kurzbz)
{
$this->addMeta('ci_method', __FUNCTION__);
// TODO(chris): IMPLEMENT!
$this->terminateWithSuccess([]);
$this->addMeta('ci_params', [
'studiensemester_kurzbz' => $studiensemester_kurzbz
]);
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
$this->PrestudentModel->addJoin(
"(
SELECT prestudent_id
FROM bis.tbl_mobilitaet
WHERE studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
) bis",
"prestudent_id"
);
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect("COALESCE(
v.semester::text,
CASE
WHEN pls.status_kurzbz IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent')
THEN pls.ausbildungssemester::text
ELSE ''::text
END
) AS semester", false);
$this->PrestudentModel->addSelect("COALESCE(v.verband::text, ''::text)");
$this->PrestudentModel->addSelect("COALESCE(v.gruppe::text, ''::text)");
$this->addSelectPrioRel();
$this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getPrestudents($studiengang_kz,
$studiensemester_kurzbz = null, $filter = null
)
{
public function getPrestudents(
$studiengang_kz,
$studiensemester_kurzbz = null,
$filter = null
) {
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiengang_kz' => $studiengang_kz,
@@ -144,10 +286,12 @@ class Students extends FHCAPI_Controller
$this->fetchPrestudents($studiengang_kz, $studiensemester_kurzbz, $filter);
}
public function getPrestudentsOrgform($studiengang_kz, $orgform_kurzbz,
$studiensemester_kurzbz = null, $filter = null
)
{
public function getPrestudentsOrgform(
$studiengang_kz,
$orgform_kurzbz,
$studiensemester_kurzbz = null,
$filter = null
) {
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiengang_kz' => $studiengang_kz,
@@ -227,7 +371,7 @@ class Students extends FHCAPI_Controller
$stg = $this->getDataOrTerminateWithError($result);
if (!$stg)
$this->terminateWithValidationErrors(['' => 'Studiengang does not exist']); // TODO(chris): phrase
$this->terminateWithSuccess([]);
$stg = current($stg);
$where['ps.status_kurzbz'] = 'Interessent';
@@ -296,7 +440,10 @@ class Students extends FHCAPI_Controller
break;
default:
if (!$studiensemester_kurzbz) {
// TODO(chris): this does not work with $orgform_kurzbz != null
/** NOTE(chris):
* show all prestudents in this stg who don't have a status
* $orgform_kurzbz does not change the results since orgform is stored in the status table
*/
$where['ps.status_kurzbz'] = null;
} else {
$this->PrestudentModel->db->where_in('ps.status_kurzbz', [
@@ -310,42 +457,18 @@ class Students extends FHCAPI_Controller
break;
}
/*
$this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
$this->PrestudentModel->addJoin('public.tbl_prestudentstatus pls', '
pls.status_kurzbz=public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.prestudent_id=tbl_prestudent.prestudent_id
AND pls.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)', 'LEFT');
$this->PrestudentModel->addJoin('lehre.tbl_studienplan sp', 'studienplan_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_prestudentstatus ps', '
ps.status_kurzbz=public.get_rolle_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ')
AND ps.prestudent_id=tbl_prestudent.prestudent_id
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->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect("
CASE WHEN ps.status_kurzbz IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent')
THEN ps.ausbildungssemester::text
ELSE ''::text END AS semester", false);
CASE
WHEN pls.status_kurzbz IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent')
THEN ps.ausbildungssemester::text
ELSE ''::text
END AS semester", false);
$this->PrestudentModel->addSelect("'' AS verband");
$this->PrestudentModel->addSelect("'' AS gruppe");
$this->addSelectPrioRel();
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->loadWhere($where);
@@ -355,10 +478,13 @@ class Students extends FHCAPI_Controller
$this->terminateWithSuccess($data);
}
public function getStudents($studiensemester_kurzbz,
$studiengang_kz, $semester = null, $verband = null, $gruppe = null
)
{
public function getStudents(
$studiensemester_kurzbz,
$studiengang_kz,
$semester = null,
$verband = null,
$gruppe = null
) {
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiensemester_kurzbz' => $studiensemester_kurzbz,
@@ -371,10 +497,14 @@ class Students extends FHCAPI_Controller
$this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, $verband, $gruppe, null, null);
}
public function getStudentsOrgform($studiensemester_kurzbz,
$studiengang_kz, $orgform_kurzbz, $semester = null, $verband = null, $gruppe = null
)
{
public function getStudentsOrgform(
$studiensemester_kurzbz,
$studiengang_kz,
$orgform_kurzbz,
$semester = null,
$verband = null,
$gruppe = null
) {
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiensemester_kurzbz' => $studiensemester_kurzbz,
@@ -388,10 +518,12 @@ class Students extends FHCAPI_Controller
$this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, $verband, $gruppe, null, $orgform_kurzbz);
}
public function getStudentsSpezialgruppe($studiensemester_kurzbz,
$studiengang_kz, $semester, $gruppe_kurzbz,
$orgform_kurzbz = null)
{
public function getStudentsSpezialgruppe(
$studiensemester_kurzbz,
$studiengang_kz,
$semester,
$gruppe_kurzbz
) {
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiensemester_kurzbz' => $studiensemester_kurzbz,
@@ -403,10 +535,13 @@ class Students extends FHCAPI_Controller
$this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, null, null, $gruppe_kurzbz, null);
}
public function getStudentsOrgformSpezialgruppe($studiensemester_kurzbz,
$orgform_kurzbz, $studiengang_kz, $semester, $gruppe_kurzbz
)
{
public function getStudentsOrgformSpezialgruppe(
$studiensemester_kurzbz,
$orgform_kurzbz,
$studiengang_kz,
$semester,
$gruppe_kurzbz
) {
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiensemester_kurzbz' => $studiensemester_kurzbz,
@@ -430,8 +565,15 @@ class Students extends FHCAPI_Controller
*
* @return void
*/
protected function fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester = null, $verband = null, $gruppe = null, $gruppe_kurzbz = null, $orgform_kurzbz = null)
{
protected function fetchStudents(
$studiensemester_kurzbz,
$studiengang_kz,
$semester = null,
$verband = null,
$gruppe = null,
$gruppe_kurzbz = null,
$orgform_kurzbz = null
) {
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
@@ -440,21 +582,6 @@ class Students extends FHCAPI_Controller
$this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester'));
}
/*
$this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
$this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id');
$this->PrestudentModel->addJoin('public.tbl_prestudentstatus pls', '
pls.status_kurzbz=public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.prestudent_id=tbl_prestudent.prestudent_id
AND pls.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)', 'LEFT');
$this->PrestudentModel->addJoin('lehre.tbl_studienplan sp', 'studienplan_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid');
$this->PrestudentModel->addJoin(
'public.tbl_studentlehrverband v',
'v.student_uid=s.student_uid AND v.studiensemester_kurzbz=' . $this->PrestudentModel->escape($studiensemester_kurzbz)
);*/
$this->prepareQuery($studiensemester_kurzbz, '');
$this->PrestudentModel->addSelect('v.semester');
@@ -462,18 +589,6 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect('v.gruppe');
$this->PrestudentModel->addSelect("'' AS priorisierung_relativ");
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$where = [];
@@ -506,7 +621,6 @@ class Students extends FHCAPI_Controller
false
);
}
}
$this->addFilter($studiensemester_kurzbz);
@@ -540,39 +654,18 @@ class Students extends FHCAPI_Controller
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
/*
$this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
$this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_prestudentstatus pls', '
pls.status_kurzbz=public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.prestudent_id=tbl_prestudent.prestudent_id
AND pls.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)', 'LEFT');
$this->PrestudentModel->addJoin('lehre.tbl_studienplan sp', 'studienplan_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid', 'LEFT');
$this->PrestudentModel->addJoin(
'public.tbl_studentlehrverband v',
'v.student_uid=s.student_uid AND v.studiensemester_kurzbz=' . $this->PrestudentModel->escape($studiensemester_kurzbz),
'LEFT'
);*/
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect("COALESCE(v.semester::text, CASE WHEN public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent') THEN public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)::text ELSE ''::text END) AS semester", false);
$this->PrestudentModel->addSelect('v.verband');
$this->PrestudentModel->addSelect('v.gruppe');
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$this->PrestudentModel->addSelect("COALESCE(
v.semester::text,
CASE
WHEN pls.status_kurzbz IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent')
THEN pls.ausbildungssemester::text
ELSE ''::text
END
) AS semester", false);
$this->PrestudentModel->addSelect("COALESCE(v.verband::text, ''::text)");
$this->PrestudentModel->addSelect("COALESCE(v.gruppe::text, ''::text)");
$this->addSelectPrioRel();
@@ -609,40 +702,12 @@ class Students extends FHCAPI_Controller
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
/*
$this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
$this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id');
$this->PrestudentModel->addJoin('public.tbl_prestudentstatus pls', '
pls.status_kurzbz=public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.prestudent_id=tbl_prestudent.prestudent_id
AND pls.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, NULL)
AND pls.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)', 'LEFT');
$this->PrestudentModel->addJoin('lehre.tbl_studienplan sp', 'studienplan_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid');
$this->PrestudentModel->addJoin(
'public.tbl_studentlehrverband v',
'v.student_uid=s.student_uid AND v.studiensemester_kurzbz=' . $this->PrestudentModel->escape($studiensemester_kurzbz),
'LEFT'
);*/
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect('v.semester');
$this->PrestudentModel->addSelect('v.verband');
$this->PrestudentModel->addSelect('v.gruppe');
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$this->addSelectPrioRel();
@@ -681,33 +746,12 @@ class Students extends FHCAPI_Controller
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
/*
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
$this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id');
$this->PrestudentModel->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid');
$this->PrestudentModel->addJoin(
'public.tbl_studentlehrverband v',
'v.student_uid=s.student_uid AND v.studiensemester_kurzbz=' . $this->PrestudentModel->escape($studiensemester_kurzbz),
'LEFT'
);*/
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect('v.semester');
$this->PrestudentModel->addSelect('v.verband');
$this->PrestudentModel->addSelect('v.gruppe');
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$this->addSelectPrioRel();
$this->addFilter($studiensemester_kurzbz);
@@ -771,6 +815,18 @@ class Students extends FHCAPI_Controller
// verband
// gruppe
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$this->PrestudentModel->addSelect('UPPER(stg.typ || stg.kurzbz) AS studiengang');
$this->PrestudentModel->addSelect('tbl_prestudent.studiengang_kz');
$this->PrestudentModel->addSelect('stg.bezeichnung AS stg_bezeichnung');
@@ -806,13 +862,6 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect('mentor');
$this->PrestudentModel->addSelect('b.aktiv AS bnaktiv');
/*$this->PrestudentModel->addSelect('tbl_prestudent.reihungstest_id');
$this->PrestudentModel->addSelect('tbl_prestudent.anmeldungreihungstest');
$this->PrestudentModel->addSelect('tbl_prestudent.gsstudientyp_kurzbz');
$this->PrestudentModel->addSelect('tbl_prestudent.priorisierung');
$this->PrestudentModel->addSelect('p.zugangscode');
$this->PrestudentModel->addSelect('p.bpk');*/
$this->PrestudentModel->db->where_in('tbl_prestudent.studiengang_kz', $this->allowedStgs);
$this->PrestudentModel->addOrder('nachname');
@@ -827,13 +876,13 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect("(
SELECT count(*)
FROM (
SELECT *, public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) AS laststatus
FROM PUBLIC.tbl_prestudent pss
JOIN PUBLIC.tbl_prestudentstatus USING (prestudent_id)
SELECT *, public.get_rolle_prestudent(pss.prestudent_id, NULL) AS laststatus
FROM public.tbl_prestudent pss
JOIN public.tbl_prestudentstatus USING (prestudent_id)
WHERE person_id = p.person_id
AND studiensemester_kurzbz = (
SELECT studiensemester_kurzbz
FROM PUBLIC.tbl_prestudentstatus
FROM public.tbl_prestudentstatus
WHERE prestudent_id = tbl_prestudent.prestudent_id
AND status_kurzbz = 'Interessent'
LIMIT 1
@@ -842,7 +891,7 @@ class Students extends FHCAPI_Controller
) prest
WHERE laststatus NOT IN ('Abbrecher', 'Abgewiesener', 'Absolvent')
AND priorisierung <= tbl_prestudent.priorisierung
) || ' (' || tbl_prestudent.priorisierung || ')' AS priorisierung_relativ", false);
) || ' (' || COALESCE(tbl_prestudent.priorisierung::text, ' '::text) || ')' AS priorisierung_relativ", false);
}
/**
@@ -854,40 +903,20 @@ class Students extends FHCAPI_Controller
*/
protected function addFilter($studiensemester_kurzbz)
{
$filter = json_decode($this->input->get('filter'), true);
$filter = $this->input->post('filter');
if (!is_array($filter))
{
$this->addMeta('addfilter', 'invalid filter: ' . $this->input->get('filter'));
$this->addMeta('addfilter', 'invalid filter: ' . json_encode($this->input->post('filter')));
return;
}
if (isset($filter['konto_count_0'])) {
$bt = $this->PrestudentModel->escape($filter['konto_count_0']);
$stdsem = $this->PrestudentModel->escape($studiensemester_kurzbz);
$this->PrestudentModel->db->where('(
SELECT count(*)
FROM public.tbl_konto
WHERE person_id=tbl_prestudent.person_id
AND buchungstyp_kurzbz=' . $bt . '
AND studiensemester_kurzbz=' . $stdsem . '
) =', 0);
$this->PrestudentModel->db->where('get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) !=', 'Incoming');
}
if (isset($filter['konto_missing_counter'])) {
$bt = $this->PrestudentModel->escape($filter['konto_missing_counter']);
$stg = '';
if ($this->variablelib->getVar('kontofilterstg') == 'true')
$stg = ' AND studiengang_kz=tbl_prestudent.studiengang_kz';
$bt = $bt == 'alle' ? '' : ' AND buchungstyp_kurzbz=' . $bt;
$this->PrestudentModel->db->where('(
SELECT sum(betrag)
FROM public.tbl_konto
WHERE person_id=tbl_prestudent.person_id' .
$bt .
$stg . '
) !=', 0);
foreach ($filter as $item) {
if (isset($item['usestdsem']) && $item['usestdsem'])
$item['studiensemester_kurzbz'] = $studiensemester_kurzbz;
if (!$this->PrestudentModel->addFilter($item)) {
$this->addMeta('addfilter', 'invalid filter: ' . json_encode($item));
return;
}
}
}
}
@@ -272,6 +272,7 @@ class Verband extends FHCAPI_Controller
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf");
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester');
$this->StudiengangModel->addSelect('verband');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
@@ -320,6 +321,8 @@ class Verband extends FHCAPI_Controller
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
$this->StudiengangModel->addSelect('v.semester');
$this->StudiengangModel->addSelect('v.verband');
$this->StudiengangModel->addSelect('gruppe');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
@@ -0,0 +1,104 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Vertrag extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getVertrag' => ['admin:r', 'assistenz:r'],
'cancelVertrag' => ['admin:r', 'assistenz:r']
]);
// Load Libraries
$this->load->library('form_validation');
// Load language phrases
$this->loadPhrases([
'ui',
'person',
'projektarbeit'
]);
// Load models
$this->load->model('accounting/Vertrag_model', 'VertragModel');
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('person/Benutzer_model', 'BenutzerModel');
// load libraries
$this->load->library('PermissionLib');
}
public function getVertrag()
{
$vertrag_id = $this->input->get('vertrag_id');
if (!isset($vertrag_id) || !is_numeric($vertrag_id))
$this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Vertrag ID']), self::ERROR_TYPE_GENERAL);
$result = $this->VertragModel->getVertragById($vertrag_id);
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
if (!hasData($result)) $this->terminateWithSuccess([]);
$vertrag = getData($result)[0];
$this->terminateWithSuccess($vertrag);
}
public function cancelVertrag()
{
$vertrag_id = $this->input->post('vertrag_id');
$person_id = $this->input->post('person_id');
if (!isset($vertrag_id) || !is_numeric($vertrag_id))
$this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Vertrag ID']), self::ERROR_TYPE_GENERAL);
if (!isset($person_id) || !is_numeric($person_id))
$this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Person ID']), self::ERROR_TYPE_GENERAL);
// * first find lehrveranstaltung_id of the contracts lehrveranstaltung
$this->VertragModel->addSelect('lehrveranstaltung_id');
$this->VertragModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id', 'LEFT');
$result = $this->VertragModel->loadWhere(['vertrag_id' => $vertrag_id]);
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
if (!hasData($result)) $this->terminateWithSuccess([]);
$lehrveranstaltung_id = getData($result)[0]->lehrveranstaltung_id;
$allOe = $this->LehrveranstaltungModel->getAllOe($lehrveranstaltung_id);
if (isError($allOe)) $this->terminateWithError(getError($allOe), self::ERROR_TYPE_GENERAL);
$allOe = hasData($allOe) ? getData($allOe) : [];
$this->addMeta('oe', $allOe);
// * then check if the user has permissions to cancel the corresponding lv-organisational units
if (!$this->permissionlib->isBerechtigtMultipleOe('admin', $allOe, 'suid') &&
!$this->permissionlib->isBerechtigtMultipleOe('lehre/lehrauftrag_bestellen', $allOe, 'suid'))
{
return $this->_outputAuthError([$this->router->method => ['admin:rw', 'lehrauftrag_bestellen:rw']]);
}
$uidResult = $this->BenutzerModel->getFromPersonId($person_id);
if (isError($uidResult)) $this->terminateWithError(getError($uidResult), self::ERROR_TYPE_GENERAL);
if (!hasData($uidResult)) $this->terminateWithError("no user found", self::ERROR_TYPE_GENERAL);
$mitarbeiter_uid = getData($uidResult)[0]->uid;
$result = $this->VertragModel->cancelVertrag($vertrag_id, $mitarbeiter_uid);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -51,13 +51,17 @@ class Vorlagen extends FHCAPI_Controller
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
$result = $this->BenutzerfunktionModel->getBenutzerfunktionByUid($uid, 'oezuordnung');
$data = $this->getDataOrTerminateWithError($result);
$oe_kurzbz = current($data);
if (hasData($result))
{
$data = getData($result);
$result = $this->VorlageModel->getAllVorlagenByOe($oe_kurzbz->oe_kurzbz);
$data = $this->getDataOrTerminateWithError($result);
$oe_kurzbz = array_column($data, 'oe_kurzbz');
$result = $this->VorlageModel->getAllVorlagenByOe($oe_kurzbz);
$this->terminateWithSuccess(hasData($result) ? getData($result) : array());
}
$this->terminateWithSuccess(array());
$this->terminateWithSuccess($data);
}
}
+22 -9
View File
@@ -70,20 +70,22 @@ abstract class Auth_Controller extends FHC_Controller
/**
* Checks for Permissions depending if the given person is a
* Mitarbeiter and/or Student
* If neither Student nor Mitarbeiter, default permissions are checked
* and exits/outputs an error if they are not met.
*
* @param integer $person_id
* @param array $permMa Perms if the person is a Mitarbeiter
* @param array $permStud Perms if the person is a Student
* @param array $permDefault Perms if the person is neither a Student nor a Mitarbeiter
*
* @return void
*/
protected function checkPermissionsForPerson($person_id, $permMa, $permStud)
protected function checkPermissionsForPerson($person_id, $permMa, $permStud, $permDefault = null)
{
$res = $this->hasPermissionsForPerson($person_id, $permMa, $permStud);
$res = $this->hasPermissionsForPerson($person_id, $permMa, $permStud, $permDefault);
if ($res) {
$perm = array_keys(array_flip(array_merge($res|1 ? $permMa : [], $res|2 ? $permStud : [])));
$perm = array_keys(array_flip(array_merge($res&1 ? $permMa : [], $res&2 ? $permStud : [], $res&4 ? $permDefault : [])));
$this->_outputAuthError([$this->router->method => $perm]);
}
}
@@ -108,16 +110,19 @@ abstract class Auth_Controller extends FHC_Controller
* Checks for Permissions depending if the given person is a
* Mitarbeiter and/or Student
* and returns the result.
*
* If neither Student nor Mitarbeiter, default permissions are checked
*
* @param integer $person_id
* @param array $permMa Perms if the person is a Mitarbeiter
* @param array $permStud Perms if the person is a Student
*
* @param array $permDefault Perms if the person is neither a Student nor a Mitarbeiter
* @return integer 0 if permission is granted
*/
protected function hasPermissionsForPerson($person_id, $permMa, $permStud)
protected function hasPermissionsForPerson($person_id, $permMa, $permStud, $permDefault)
{
$res = 3;
$res = 8;
$isMitarbeiter = false;
$isStudent = false;
$this->load->model('person/Person_model', 'PersonModel');
$this->PersonModel->addJoin('public.tbl_benutzer', 'person_id');
$this->PersonModel->addJoin('public.tbl_mitarbeiter', 'uid = mitarbeiter_uid');
@@ -125,7 +130,8 @@ abstract class Auth_Controller extends FHC_Controller
if (hasData($result)) {
if ($this->permissionlib->isEntitled(['a' => $permMa], 'a'))
return 0;
$res = 1;
$isMitarbeiter = true;
$res += 1;
}
$this->PersonModel->addJoin('public.tbl_prestudent', 'person_id');
$result = $this->PersonModel->load($person_id);
@@ -140,8 +146,15 @@ abstract class Auth_Controller extends FHC_Controller
return 0;
}
}
$isStudent = true;
$res += 2;
}
if (isset($permDefault) && !$isMitarbeiter && !$isStudent)
{
if ($this->permissionlib->isEntitled(['a' => $permDefault], 'a'))
return 0;
$res += 4;
}
return $res;
}
+70
View File
@@ -515,3 +515,73 @@ function has_permissions_for_stg($studiengang_kz, $permissions = '')
return false;
}
/**
* check if an entry exists in the database
*/
function is_in_db($key, $model = '')
{
if (!$model)
return false;
$field = strstr($model, ":");
if ($field) {
$model = strstr($model, ":", true);
$field = substr($field, 1);
}
$CI =& get_instance();
$CI->load->model($model, $model);
if ($field) {
$result = $CI->$model->loadWhere([
$field => $key
]);
} else {
$result = $CI->$model->load($key);
}
return (isSuccess($result) && hasData($result));
}
/**
* is building an array for Dropdown Entry in Print Dropdown
* @param $id id for the Document to add to the Document Array
* @param $name title of the dropdownEntry
* @param $parameterUrl url of parameters xml, xsl, format etc as needed
* WITHOUT BASEURL eg. "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf"
* @param $uid default parameter, if null only parameterurl will be added
* additional needed parameter: put in the parameterUrl
* @param $alternativeBaseUrl: if baseUrl not pdfExport.php, put here alternative without ? char, eg. "zutrittskarte.php"
*
* @return Array
*/
function buildDropdownEntryPrintArray($id, $name, $parameterurl, $uid=null, $order=null, $alternativeBaseUrl=null)
{
//DEFAULT BASEURL
$baseurl = "pdfExport.php?";
$uidString = $uid ? "&uid=" . $uid : "";
if($alternativeBaseUrl)
{
return [
"id" => $id,
"type" => "documenturl",
"name" => $name,
"url" => $alternativeBaseUrl . "?" . $parameterurl . $uidString,
"order" => $order
];
}
else
return [
"id" => $id,
"type" => "documenturl",
"name" => $name,
"url" => $baseurl . $parameterurl . "&uid=" . $uid,
"order" => $order
];
}
@@ -246,3 +246,10 @@ function generateSkipLink($skipID)
$toPrint.='" class="fhcSkipLink" aria-label="Skip to main content"></a>';
echo $toPrint;
}
function absoluteJsImportUrl($relurl)
{
$ci =& get_instance();
$url = base_url($relurl) . '?'. $ci->config->item('fhcomplete_build_version');
return $url;
}
@@ -41,3 +41,4 @@ if (!defined('BASEPATH')) exit('No direct script access allowed');
$lang['form_validation_has_write_permissions'] = 'You have no rights to edit {field} field.';
$lang['form_validation_is_valid_date'] = 'The date format is invalid or out of range.';
$lang['form_validation_has_permissions_for_stg'] = 'You have no rights for stg {field}.';
$lang['form_validation_is_in_db'] = '{field} does not exist.';
@@ -402,6 +402,26 @@ class Vertrag_model extends DB_Model
return $this->loadWhere(array('mitarbeiter_uid' => $mitarbeiter_uid, 'lehreinheit_id' => $lehreinheit_id));
}
public function getVertragById($vertrag_id)
{
$this->addSelect(
'tbl_vertrag.vertrag_id, vertragstyp_kurzbz, vertragsstunden, vertragsstunden_studiensemester_kurzbz, status.vertragsstatus_kurzbz,
status.bezeichnung AS vertragsstatus, tbl_vertrag.betrag, lema.semesterstunden, lema.stundensatz'
);
$this->addJoin('lehre.tbl_lehreinheitmitarbeiter lema', 'tbl_vertrag.vertrag_id = lema.vertrag_id', 'LEFT');
$this->addJoin('
(
SELECT DISTINCT ON(vst.vertrag_id) vst.vertrag_id,
bezeichnung,
tbl_vertragsstatus.vertragsstatus_kurzbz
FROM lehre.tbl_vertrag_vertragsstatus vst
JOIN lehre.tbl_vertragsstatus USING(vertragsstatus_kurzbz)
ORDER BY vst.vertrag_id, datum DESC
) as status', 'status.vertrag_id = lehre.tbl_vertrag.vertrag_id', 'LEFT');
return $this->loadWhere(['tbl_vertrag.vertrag_id' => $vertrag_id]);
}
public function cancelVertrag($vertrag_id, $mitarbeiter_uid)
{
$vertrag = $this->load($vertrag_id);
+116
View File
@@ -1,5 +1,7 @@
<?php
use CI3_Events as Events;
class Prestudent_model extends DB_Model
{
/**
@@ -782,4 +784,118 @@ class Prestudent_model extends DB_Model
return $this->execQuery($query, array($person_id));
}
/**
* Adds a filter to the query builder
*
* @param array $filter
* @return boolean
*/
public function addFilter($filter)
{
if (!isset($filter['type']))
return false;
switch ($filter['type']) {
case 'konto':
$bt = '';
$stdsem = '';
$comp = '!=';
if (isset($filter['buchungstyp_kurzbz']) && $filter['buchungstyp_kurzbz'] != 'all')
$bt = ' AND buchungstyp_kurzbz=' . $this->escape($filter['buchungstyp_kurzbz']);
if (isset($filter['studiensemester_kurzbz']))
$stdsem = ' AND studiensemester_kurzbz=' . $this->escape($filter['studiensemester_kurzbz']);
if (isset($filter['missing']) && $filter['missing']) {
$comp = '=';
$this->db->where('get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) !=', 'Incoming');
}
$this->db->where('(
SELECT count(*)
FROM public.tbl_konto
WHERE person_id=tbl_prestudent.person_id
' . $bt . '
' . $stdsem . '
) ' . $comp, 0);
break;
case 'konto_counter':
$bt = '';
$samestg = '';
$past = '';
if (isset($filter['buchungstyp_kurzbz']) && $filter['buchungstyp_kurzbz'] != 'all')
$bt = ' AND buchungstyp_kurzbz = ' . $this->escape($filter['buchungstyp_kurzbz']);
if (isset($filter['samestg']) && $filter['samestg'])
$samestg = ' AND studiengang_kz = tbl_prestudent.studiengang_kz';
if (isset($filter['past']) && $filter['past'])
$past = ' AND buchungsdatum < NOW()';
$this->db->where('(
SELECT sum(betrag)
FROM public.tbl_konto
WHERE person_id = tbl_prestudent.person_id
' . $bt . '
' . $samestg . '
' . $past . '
) !=', 0);
break;
case 'zgv':
$this->db
->group_start()
->group_start()
->where('zgv_code IS NOT NULL')
->where('zgvdatum IS NULL')
->group_end()
->or_group_start()
->where('zgvmas_code IS NOT NULL')
->where('zgvmadatum IS NULL')
->group_end()
->or_group_start()
->where('zgvdoktor_code IS NOT NULL')
->where('zgvdoktordatum IS NULL')
->group_end()
->group_end();
break;
case 'documents':
$this->db->where('(
SELECT count(*)
FROM public.tbl_dokumentstudiengang
WHERE dokument_kurzbz NOT IN (
SELECT dokument_kurzbz
FROM tbl_dokumentprestudent
WHERE prestudent_id=tbl_prestudent.prestudent_id
)
AND studiengang_kz=tbl_prestudent.studiengang_kz
) !=', 0);
break;
case 'statusgrund':
if (!isset($filter['statusgrund_id']))
return false;
if (isset($filter['studiensemester_kurzbz']))
$stdsem = ' AND studiensemester_kurzbz=' . $this->escape($filter['studiensemester_kurzbz']);
$this->db->where('(
SELECT count(*)
FROM public.tbl_prestudentstatus
WHERE prestudent_id = tbl_prestudent.prestudent_id
AND statusgrund_id = ' . $this->escape($filter['statusgrund_id']) . '
' . $stdsem . '
) !=', 0);
break;
}
Events::trigger('prestudent_add_filter', $filter);
return true;
}
}
@@ -1286,4 +1286,61 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execReadOnlyQuery($qry, $params);
}
/**
* Gets Lehrveranstaltungen for a student, as needed for a Projektarbeit.
* @param student_uid
* @param studiengang_kz optional, all Lvs of this Studiengang will be included
* @param additional_lehrveranstaltung_id optional, this lv will be added to result
* @return object success or error
*/
public function getLvsForProjektarbeit($student_uid, $studiengang_kz = null, $additional_lehrveranstaltung_id = null)
{
$params = array($student_uid, $student_uid);
$qry = "
SELECT *
FROM
lehre.tbl_lehrveranstaltung
WHERE
(
lehrveranstaltung_id IN (
SELECT
lehrveranstaltung_id
FROM
campus.vw_student_lehrveranstaltung
WHERE
uid=?
UNION
SELECT
lehrveranstaltung_id
FROM
lehre.tbl_zeugnisnote
WHERE
student_uid=?
)";
if (isset($studiengang_kz))
{
$params[] = $studiengang_kz;
$qry .= " OR (studiengang_kz = ? AND semester IS NOT NULL)";
}
if (isset($additional_lehrveranstaltung_id))
{
$params[] = $additional_lehrveranstaltung_id;
$qry .= " OR lehrveranstaltung_id = ?";
}
$qry .= "
)
AND projektarbeit = TRUE
ORDER BY
semester, bezeichnung";
return $this->execQuery($qry, $params);
}
}
@@ -24,15 +24,16 @@ class Projektarbeit_model extends DB_Model
public function getProjektarbeit($student_uid, $studiengang_kz = null, $studiensemester_kurzbz = null, $projekttyp = null, $final = null)
{
$qry = "SELECT
tbl_projektarbeit.* , tbl_projekttyp.bezeichnung
tbl_projektarbeit.*, tbl_projekttyp.bezeichnung,
tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_firma.name AS firma_name
FROM
lehre.tbl_projektarbeit
JOIN
lehre.tbl_projekttyp USING (projekttyp_kurzbz), lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_firma USING (firma_id)
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.student_uid = ?";
$params = array($student_uid);
@@ -261,4 +262,30 @@ class Projektarbeit_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($projektarbeit_id));
}
/**
*
* @param
* @return object success or error
*/
public function hasBerechtigungForProjektarbeit($projektarbeit_id)
{
if (!$projektarbeit_id || !is_numeric($projektarbeit_id))
return false;
$this->ProjektarbeitModel->addSelect('studiengang_kz');
$this->ProjektarbeitModel->addJoin('public.tbl_student', 'student_uid');
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
if (isError($result) || !hasData($result))
return false;
$studiengang_kz = getData($result)[0]->studiengang_kz;
if ($this->permissionlib->isBerechtigt('admin', 'suid', $studiengang_kz))
return true;
if ($this->permissionlib->isBerechtigt('assistenz', 'suid', $studiengang_kz))
return true;
return false;
}
}
@@ -10,6 +10,7 @@ class Projektbetreuer_model extends DB_Model
parent::__construct();
$this->dbTable = 'lehre.tbl_projektbetreuer';
$this->pk = array('betreuerart_kurzbz', 'projektarbeit_id', 'person_id');
$this->hasSequence = false;
}
/**
@@ -157,7 +157,6 @@ class Studienplan_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($lv_id));
}
public function getStudienplaeneForPerson($person_id)
{
$this->addDistinct();
@@ -207,7 +207,6 @@ class Notiz_model extends DB_Model
";
return $this->execQuery($qry, array($type, $id));
}
+14 -5
View File
@@ -151,12 +151,21 @@ class Person_model extends DB_Model
*/
public function searchPerson($filter)
{
$this->addSelect('vorname, nachname, gebdatum, person_id');
$this->addSelect('vorname, nachname, gebdatum, person_id, titelpre, titelpost');
$this->addSelect("CASE
WHEN EXISTS
(SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid) WHERE person_id=tbl_person.person_id)
THEN 'Mitarbeiter'
WHEN EXISTS
(SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_student ON(uid=student_uid) WHERE person_id=tbl_person.person_id)
THEN 'Student'
ELSE 'Person'
END AS status");
$result = $this->loadWhere(
'lower(nachname) like '.$this->db->escape('%'.$filter.'%')."
'lower(nachname) like '.$this->db->escape('%'.mb_strtolower($filter).'%')."
OR lower(vorname) like ".$this->db->escape('%'.$filter.'%')."
OR lower(nachname || ' ' || vorname) like ".$this->db->escape('%'.$filter.'%')."
OR lower(vorname || ' ' || nachname) like ".$this->db->escape('%'.$filter.'%')
OR lower(nachname || ' ' || vorname) like ".$this->db->escape('%'.mb_strtolower($filter).'%')."
OR lower(vorname || ' ' || nachname) like ".$this->db->escape('%'.mb_strtolower($filter).'%')
);
return $result;
@@ -411,4 +420,4 @@ class Person_model extends DB_Model
return success($result);
}
}
}
}
+14 -7
View File
@@ -12,17 +12,24 @@ class Firma_model extends DB_Model
$this->pk = 'firma_id';
}
public function searchFirmen($filter)
public function searchFirmen($filter, $aktiv = null)
{
$params = [];
$filter = strtoLower($filter);
$qry = "
SELECT
SELECT
f.name, f.firma_id
FROM
public.tbl_firma f
WHERE
lower (f.name) LIKE '%". $this->db->escape_like_str($filter)."%'";
FROM
public.tbl_firma f
WHERE
lower (f.name) LIKE '%". $this->db->escape_like_str($filter)."%'";
return $this->execQuery($qry);
if (isset($aktiv) && is_bool($aktiv))
{
$params[] = $aktiv;
$qry .= " AND aktiv = ?";
}
return $this->execQuery($qry, $params);
}
}
@@ -353,12 +353,14 @@ class Mitarbeiter_model extends DB_Model
{
$filter = strtoLower($filter);
$returnwert = "p.person_id, p.nachname, p.vorname, p.titelpost, p.titelpre";
if ($mode == "mitAkadGrad")
$returnwert = "ma.mitarbeiter_uid, CONCAT(p.nachname, ' ', p.vorname, ' ', p.titelpost, ' ', p.titelpre, ' (', ma.mitarbeiter_uid , ')') as mitarbeiter";
$returnwert .= ", ma.mitarbeiter_uid, CONCAT(p.nachname, ' ', p.vorname, ' ', p.titelpost, ' ', p.titelpre, ' (', ma.mitarbeiter_uid , ')') as mitarbeiter";
elseif ($mode == "ohneMaUid")
$returnwert = "p.person_id, CONCAT(p.nachname, ' ', p.vorname, ' ', p.titelpost, ' ', p.titelpre) as mitarbeiter";
$returnwert .= ", CONCAT(p.nachname, ' ', p.vorname, ' ', p.titelpost, ' ', p.titelpre) as mitarbeiter";
else
$returnwert = "ma.mitarbeiter_uid, CONCAT(p.nachname, ' ', p.vorname, ' (', ma.mitarbeiter_uid , ')') as mitarbeiter";
$returnwert .= ", ma.mitarbeiter_uid, CONCAT(p.nachname, ' ', p.vorname, ' (', ma.mitarbeiter_uid , ')') as mitarbeiter";
$qry = "
SELECT " . $returnwert . "
@@ -373,7 +375,11 @@ class Mitarbeiter_model extends DB_Model
OR
lower (p.vorname) LIKE '%". $this->db->escape_like_str($filter)."%'
OR
(ma.mitarbeiter_uid) LIKE '%". $this->db->escape_like_str($filter)."%'";
(ma.mitarbeiter_uid) LIKE '%". $this->db->escape_like_str($filter)."%'
OR
lower(vorname || ' ' || nachname || ' ' || vorname) like ".$this->db->escape('%'.mb_strtolower($filter).'%')."
ORDER BY
p.nachname, p.vorname, b.uid, p.person_id";
return $this->execQuery($qry);
}
@@ -2,7 +2,7 @@
class Stundensatz_model extends DB_Model
{
/**
* Constructor
*/
@@ -43,6 +43,97 @@ class Stundensatz_model extends DB_Model
return $this->execQuery($qry, $params);
}
public function getStundensatzForMitarbeiter($person_id, $studiensemester_kurzbz)
{
$this->load->config('stv');
$useFixangestelltStundensatz = $this->config->item('tabs')['projektarbeit']['lvLektroinnenzuteilungFixangestelltStundensatz'];
$defaultStundensatz = $this->config->item('tabs')['projektarbeit']['defaultProjektbetreuerStundensatz'];
$stundensatz = '';
if(isset($person_id) && isset($studiensemester_kurzbz))
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->StudiensemesterModel->addSelect('start, ende');
$result = $this->StudiensemesterModel->load($studiensemester_kurzbz);
if (hasData($result))
{
$studiensemester = getData($result)[0];
if (isset($useFixangestelltStundensatz) && !$useFixangestelltStundensatz)
{
// load Mitarbeiter
$params = [$person_id];
$qry = "
SELECT
mitarbeiter_uid, fixangestellt
FROM
public.tbl_mitarbeiter
JOIN public.tbl_benutzer ON(tbl_benutzer.uid=tbl_mitarbeiter.mitarbeiter_uid)
WHERE
person_id=?
ORDER BY
tbl_mitarbeiter.insertamum DESC NULLS LAST
LIMIT 1";
$result = $this->execQuery($qry, $params);
if (hasData($result))
{
foreach (getData($result) as $ma)
{
if (!$ma->fixangestellt)
{
$stundensatzRes = $this->getStundensatzByDatum(
$ma->mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'lehre'
);
if (hasData($stundensatzRes))
$stundensatz = getData($stundensatzRes)[0]->stundensatz;
else
$stundensatz = '0.00';
}
}
}
else
{
$stundensatz = '0.00';
}
}
else
{
$params = [$person_id, $studiensemester->ende, $studiensemester->start];
$qry = "SELECT ss.stundensatz
FROM hr.tbl_stundensatz ss
JOIN public.tbl_mitarbeiter ON ss.uid = tbl_mitarbeiter.mitarbeiter_uid
JOIN public.tbl_benutzer ON(tbl_benutzer.uid=tbl_mitarbeiter.mitarbeiter_uid)
WHERE person_id=?
AND stundensatztyp = 'lehre'
AND gueltig_von <= ?
AND (gueltig_bis >= ? OR gueltig_bis IS NULL)
ORDER BY gueltig_bis DESC NULLS FIRST, gueltig_von DESC NULLS LAST LIMIT 1";
$result = $this->execQuery($qry, $params);
if (hasData($result))
{
$stundensatz = getData($result)[0]->stundensatz;
}
else
{
$stundensatz = $defaultStundensatz;
}
}
}
}
return $stundensatz;
}
public function getDefaultStundensatz($mitarbeiter_uid, $beginn, $ende = null, $typ = null)
{
$stundensatz_result = $this->getStundensatzByDatum($mitarbeiter_uid, $beginn, $ende, $typ);
@@ -58,4 +149,4 @@ class Stundensatz_model extends DB_Model
}
return $default_stundensatz;
}
}
}
+2 -9
View File
@@ -107,16 +107,10 @@ if(isset($_GET['getAnmeldung']))
$abgeschlossen = false;
$semesterlock = false;
$regelerfuellt = true;
if ($kompatible_lv)
{
$lvregelExists = $lvregel->exists($kompatible_lv);
if($lvregelExists)
{
if($lvregel->isAbgeschlossen($uid, $kompatible_lv))
$abgeschlossen=true;
else
$abgeschlossen=false;
}
if(!$lvregel->checkSemester($kompatible_lv, $semester))
{
@@ -135,7 +129,7 @@ if(isset($_GET['getAnmeldung']))
}
}
if (!(($lvregelExists && !$abgeschlossen) || $semesterlock || !$regelerfuellt))
if (!$semesterlock && $regelerfuellt)
{
$anzahl++;
// LV wird angeboten und Anmeldefenster ist offen
@@ -152,7 +146,6 @@ if(isset($_GET['getAnmeldung']))
echo '<br><input type="radio" disabled="true" value="'.$lvid.'" name="lv" /><span class="ok">'.$lv->bezeichnung.'</span><img src="../../../skin/images/information.png" title="'.$p->t('studienplan/bereitsAngemeldet').'"/>';
}
}
}
/* else
{
+6 -4
View File
@@ -1,15 +1,21 @@
@import './Fhc.css';
@import './SvgIcons.css';
@import './components/searchbar/searchbar.css';
@import './components/verticalsplit.css';
@import './components/FilterComponent.css';
@import './components/Tabs.css';
@import './components/Notiz.css';
@import './components/Messages.css';
@import './components/AppMenu.css';
html {
font-size: .875em;
}
#appMenu {
width: 300px;
}
.navbar-dark .navbar-brand:focus {
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
z-index: 3;
@@ -37,10 +43,6 @@ html {
flex: 1 1 auto;
}
#sidebarMenu {
width: 0%;
}
.tabulator-row.disabled.tabulator-row-odd .tabulator-cell {
color: var(--gray-400);
}
+28
View File
@@ -0,0 +1,28 @@
/* Themable Variables */
:root {
--svg-icon-apps: var(--fhc-icon-apps, url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">\
<circle cx="15" cy="15" r="15"/>\
<circle cx="15" cy="50" r="15"/>\
<circle cx="15" cy="85" r="15"/>\
<circle cx="50" cy="15" r="15"/>\
<circle cx="50" cy="50" r="15"/>\
<circle cx="50" cy="85" r="15"/>\
<circle cx="85" cy="15" r="15"/>\
<circle cx="85" cy="50" r="15"/>\
<circle cx="85" cy="85" r="15"/>\
</svg>'));
}
.svg-icon {
display: inline-block;
width: 1rem;
line-height: 1;
background-color: currentColor;
}
.svg-icon:before {
content: "\00a0";
}
.svg-icon.svg-icon-apps {
-webkit-mask-image: var(--svg-icon-apps);
mask-image: var(--svg-icon-apps);
}
+26
View File
@@ -0,0 +1,26 @@
.fhc-app-menu {
display: flex;
flex-direction: column;
padding-left: 0;
margin: calc(var(--bs-offcanvas-padding-y) * -1) calc(var(--bs-offcanvas-padding-x) * -1);
}
.fhc-app-menu li {
display: block;
border: var(--bs-border-width) solid var(--bs-border-color);
}
.fhc-app-menu li + li {
border-top-width: 0;
}
.fhc-app-menu li a {
display: block;
padding: .5rem 1rem;
text-decoration: none;
}
.fhc-app-menu li a.active,
.fhc-app-menu li a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
background: var(--surface-hover);
}
.fhc-app-menu li a.active {
pointer-events: none;
}
+29
View File
@@ -5690,3 +5690,32 @@
outline: 0;
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
}
/* input-group */
/* autocomplete */
.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3) > .p-autocomplete-input,
.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control > .p-autocomplete-input,
.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select > .p-autocomplete-input,
.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > .p-autocomplete-input,
.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3) > .p-autocomplete > .p-autocomplete-input,
.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control > .p-autocomplete > .p-autocomplete-input,
.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select > .p-autocomplete > .p-autocomplete-input,
.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > .p-autocomplete > .p-autocomplete-input {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > .p-autocomplete-input,
.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > .p-autocomplete > .p-autocomplete-input {
margin-left: calc(var(--bs-border-width) * -1);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.p-inputtext.is-invalid:enabled:hover {
border-color: var(--bs-form-invalid-border-color);
}
.p-inputtext.is-invalid:enabled:focus,
.was-validated .p-inputtext:invalid:focus {
border-color: var(--bs-form-invalid-border-color);
box-shadow: 0 0 0 .25rem rgba(var(--bs-danger-rgb),.25);
}
+2 -2
View File
@@ -62,10 +62,10 @@ export default {
url: 'api/frontend/v1/messages/messages/getUid/' + userParams.id + '/' + userParams.type_id
};
},
getVorlagentext(vorlage_kurzbz){
getDataVorlage(vorlage_kurzbz){
return {
method: 'get',
url: 'api/frontend/v1/messages/messages/getVorlagentext/' + vorlage_kurzbz
url: 'api/frontend/v1/messages/messages/getDataVorlage/' + vorlage_kurzbz
};
},
getNameOfDefaultRecipient(params){
+1 -1
View File
@@ -34,7 +34,7 @@ export default {
url: '/api/frontend/v1/studstatus/leitung/getHistory/' + antrag_id
};
},
getPrestudents(query, signal) {
getPrestudents(query) {
return {
method: 'post',
url: '/api/frontend/v1/studstatus/leitung/getPrestudents',
+8 -11
View File
@@ -86,13 +86,15 @@ export default {
getMitarbeiter(searchString) {
return {
method: 'get',
url: 'api/frontend/v1/stv/abschlusspruefung/getMitarbeiter/' + searchString
url: 'api/frontend/v1/stv/abschlusspruefung/getMitarbeiter',
params: { searchString }
};
},
getPruefer(searchString) {
return {
method: 'get',
url: 'api/frontend/v1/stv/abschlusspruefung/getPruefer/' + searchString
url: 'api/frontend/v1/stv/abschlusspruefung/getPruefer',
params: { searchString }
};
},
getNoten() {
@@ -108,16 +110,11 @@ export default {
params: { uids }
};
},
getAllMitarbeiter(){
searchPerson(searchString) {
return {
method: 'get',
url: 'api/frontend/v1/stv/abschlusspruefung/getAllMitarbeiter/'
url: 'api/frontend/v1/stv/abschlusspruefung/searchPerson/',
params: { id }
};
},
getAllPersons(){
return {
method: 'get',
url: 'api/frontend/v1/stv/abschlusspruefung/getAllPersons/'
};
}
};
};
+6
View File
@@ -16,6 +16,12 @@
*/
export default {
configFilter() {
return {
method: 'get',
url: 'api/frontend/v1/stv/config/filter'
};
},
configStudent() {
return {
method: 'get',
+1 -1
View File
@@ -39,5 +39,5 @@ export default {
url: 'api/frontend/v1/stv/archiv/delete',
params: {akte_id, studiengang_kz}
};
}
},
};
+10
View File
@@ -35,4 +35,14 @@ export default {
params
};
},
saveStudent(student_uid, studiensemester_kurzbz, params) {
return {
method: 'post',
url: 'api/frontend/v1/stv/student/saveStudent/'
+ encodeURIComponent(student_uid)
+ '/'
+ encodeURIComponent(studiensemester_kurzbz),
params
};
}
};
+13
View File
@@ -73,4 +73,17 @@ export default {
params
};
},
getDocumentDropdown(params){
return {
method: 'get',
url: 'api/frontend/v1/stv/dokumente/getDocumentDropDown/' + params.prestudent_id + '/' + params.studiensemester_kurzbz + '/' + params.studiengang_kz,
};
},
getDocumentDropdownMulti(studentUids, params){
return {
method: 'get',
url: 'api/frontend/v1/stv/dokumente/getDocumentDropDownMulti/' + params.studiensemester_kurzbz + '/' + params.studiengang_kz,
params: {studentUids}
};
}
}
+21
View File
@@ -16,6 +16,27 @@
*/
export default {
add(uid, gruppe_kurzbz, studiensemester_kurzbz) {
return {
method: 'post',
url: 'api/frontend/v1/stv/gruppen/add/',
params: {
uid,
gruppe_kurzbz,
studiensemester_kurzbz
}
};
},
search(query, studiengang_kz) {
return {
method: 'post',
url: 'api/frontend/v1/stv/gruppen/search/',
params: {
query,
studiengang_kz
}
};
},
getGruppen(id) {
return {
method: 'get',
+31
View File
@@ -0,0 +1,31 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
hasOrgforms(studiengang_kz) {
return {
method: 'get',
url: 'api/frontend/v1/stv/lehrverband/hasOrgforms/' + studiengang_kz
};
},
getTree(studiengang_kz) {
return {
method: 'get',
url: 'api/frontend/v1/stv/lehrverband/getTree/' + studiengang_kz
};
}
};
@@ -0,0 +1,80 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getProjektarbeit(uid) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektarbeit/getProjektarbeit',
params: { uid }
};
},
getTypenProjektarbeit() {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektarbeit/getTypenProjektarbeit'
};
},
getFirmen(searchString) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektarbeit/getFirmen',
params: {searchString}
};
},
getLehrveranstaltungen(student_uid, studiengang_kz, studiensemester_kurzbz, additional_lehrveranstaltung_id) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektarbeit/getLehrveranstaltungen',
params: { student_uid, studiengang_kz, studiensemester_kurzbz, additional_lehrveranstaltung_id }
};
},
getNoten() {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektarbeit/getNoten'
};
},
loadProjektarbeit(projektarbeit_id) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektarbeit/loadProjektarbeit',
params: { projektarbeit_id }
};
},
addNewProjektarbeit(params) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektarbeit/insertProjektarbeit',
params
};
},
updateProjektarbeit(params) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektarbeit/updateProjektarbeit',
params
};
},
deleteProjektarbeit(projektarbeit_id) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektarbeit/deleteProjektarbeit',
params: { projektarbeit_id }
};
}
};
@@ -0,0 +1,80 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getProjektbetreuer(projektarbeit_id ) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getProjektbetreuer',
params: { projektarbeit_id }
};
},
getBetreuerarten() {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getBetreuerarten'
};
},
getDefaultStundensaetze(person_id, studiensemester_kurzbz) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getDefaultStundensaetze',
params: { person_id, studiensemester_kurzbz }
};
},
getNoten() {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getNoten'
};
},
saveProjektbetreuer(projektarbeit_id, projektbetreuer) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektbetreuer/saveProjektbetreuer',
params: { projektarbeit_id, projektbetreuer }
};
},
deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektbetreuer/deleteProjektbetreuer',
params: { projektarbeit_id, person_id, betreuerart_kurzbz }
};
},
getProjektbetreuerBySearchQuery(searchString) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getProjektbetreuerBySearchQuery',
params: { searchString }
};
},
getPerson(person_id) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getPerson',
params: { person_id }
};
},
validateProjektbetreuer(projektbetreuer) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektbetreuer/validateProjektbetreuer',
params: { projektbetreuer }
};
}
};
+8 -1
View File
@@ -58,5 +58,12 @@ export default {
url: 'api/frontend/v1/stv/student/check',
params
};
},
add(params) {
return {
method: 'post',
url: 'api/frontend/v1/stv/student/add',
params
};
}
};
};
+20
View File
@@ -0,0 +1,20 @@
export default {
getVertrag(vertrag_id)
{
return {
method: 'get',
url: 'api/frontend/v1/stv/vertrag/getVertrag',
params: { vertrag_id },
};
},
cancelVertrag(data)
{
return {
method: 'post',
url: '/api/frontend/v1/stv/vertrag/cancelVertrag/',
params: data
};
}
}
+2 -2
View File
@@ -20,8 +20,8 @@ export default {
getUid(params){
return this.$fhcApi.get('api/frontend/v1/messages/messages/getUid/'+ params.id + '/' + params.type_id);
},
getVorlagentext(vorlage_kurzbz){
return this.$fhcApi.get('api/frontend/v1/messages/messages/getVorlagentext/' + vorlage_kurzbz);
getDataVorlage(vorlage_kurzbz){
return this.$fhcApi.get('api/frontend/v1/messages/messages/getDataVorlage/' + vorlage_kurzbz);
},
getNameOfDefaultRecipient(params){
return this.$fhcApi.get('api/frontend/v1/messages/messages/getNameOfDefaultRecipient/' + params.id + '/' + params.type_id);
+2 -1
View File
@@ -19,6 +19,7 @@ import Studium from "../../components/Cis/Studium/Studium.js";
import ApiRenderers from '../../api/factory/renderers.js';
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -320,7 +321,7 @@ const app = Vue.createApp({
// kind of a bandaid for bad css on some pages to avoid horizontal scroll
setScrollbarWidth();
app.config.globalProperties.$capitalize = capitalize;
app.use(router);
app.use(primevue.config.default, {
zIndex: {
+68
View File
@@ -0,0 +1,68 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import ApiNavigation from '../api/factory/navigation.js';
export default {
name: 'AppMenu',
props: {
appIdentifier: {
type: String,
required: true
},
navigationPage: {
type: String,
default: 'apps'
}
},
data() {
return {
items: []
};
},
watch: {
navigationPage() {
this.getItems();
}
},
methods: {
getItems() {
this.$api
.call(ApiNavigation.getMenu(this.navigationPage))
.then(result => {
this.items = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
created() {
this.getItems();
},
template: /* html */`
<ul class="fhc-app-menu">
<li v-for="(menu, key) in items" :key="key">
<a
:href="menu.link"
@click="menu.onClickCall"
:class="{ active: key === appIdentifier }"
>
<i v-if="menu.icon" class="fa fa-fw" :class="'fa-'+ menu.icon" />
{{ menu.description }}
</a>
</li>
</ul>`
};
@@ -96,8 +96,11 @@ export default {
},
methods: {
onDragstart(evt) {
DragAndDrop.setTransferData(evt.detail.originalEvent, evt.detail.item.orig);
this.draggedInternalEvent = evt.detail.item;
const data = DragAndDrop.convertToTransferData(evt.detail.item.orig);
if (DragAndDrop.isValidDragObject(data)) {
DragAndDrop.setTransferData(evt.detail.originalEvent, data);
this.draggedInternalEvent = evt.detail.item;
}
},
onDragend() {
this.draggedInternalEvent = null;
@@ -100,6 +100,11 @@ export default {
link_element.href = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/CisVue/Cms/getRoomInformation/" + room_name;
link_element.appendChild(title.cloneNode(true));
title.replaceWith(link_element);
let lvplanlinks = document.querySelectorAll('.menubox a[href*="stpl_week.php"]');
for(let lvplanlink of lvplanlinks) {
lvplanlink.href = link_element.href;
}
}
else
{
@@ -121,7 +121,8 @@ export default {
class="d-flex flex-column align-items-center h-100 position-relative d-inline-block"
>
<img
class="d-block h-100 rounded"
class="d-block rounded"
style="height: 84px;"
alt="Profilbild"
:src="getFotoSrc(person.foto)"
/>
@@ -175,7 +176,8 @@ export default {
<div class="col-md-2 d-flex justify-content-start align-items-center w-30 pb-3 gap-3 position-relative"
style="max-height: 8rem; max-width: 6rem; overflow: hidden;">
<img
class="d-block h-100 rounded"
class="d-block rounded"
style="height: 84px;"
alt="Profilbild"
:src="'data:image/jpeg;base64,' + headerDataMa.foto"
/>
@@ -1,5 +1,6 @@
import CoreSearchbar from "../searchbar/searchbar.js";
import VerticalSplit from "../verticalsplit/verticalsplit.js";
import AppMenu from "../AppMenu.js";
import StvVerband from "../Stv/Studentenverwaltung/Verband.js";
import StvStudiensemester from "../Stv/Studentenverwaltung/Studiensemester.js";
import LvTable from "./Setup/Table.js";
@@ -17,6 +18,7 @@ export default {
components: {
CoreSearchbar,
VerticalSplit,
AppMenu,
StvVerband,
StvStudiensemester,
LvTable,
@@ -247,14 +249,47 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
template: `
template: /* html */`
<div class="stv">
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-4 col-lg-3 col-xl-2 me-0 px-3">LV Verwaltung</a>
<div class="col-md-4 col-lg-3 col-xl-2 d-flex align-items-center">
<button
class="btn btn-outline-light border-0 m-1 collapsed"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#appMenu"
aria-controls="appMenu"
aria-expanded="false"
:aria-label="$p.t('ui/toggle_nav')"
>
<span class="svg-icon svg-icon-apps"></span>
</button>
<a class="navbar-brand me-0">LV Verwaltung</a>
</div>
<button
class="btn btn-outline-light border-0 d-md-none m-1 collapsed"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#sidebarMenu"
aria-controls="sidebarMenu"
aria-expanded="false"
:aria-label="$p.t('ui/toggle_nav')"
>
<span class="fa-solid fa-table-list"></span>
</button>
<core-searchbar :searchoptions="searchbaroptions" :searchfunction=searchfunction class="searchbar w-100"></core-searchbar>
</header>
<div class="container-fluid overflow-hidden">
<div class="row h-100">
<aside id="appMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<div class="offcanvas-header">
LV Verwaltung
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<div class="offcanvas-body">
<app-menu app-identifier="lvv" />
</div>
</aside>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
@@ -136,17 +136,13 @@ export default {
}
);
},
getVorlagentext(vorlage_kurzbz){
getDataVorlage(vorlage_kurzbz){
return this.$api
.call(this.endpoint.getVorlagentext(vorlage_kurzbz))
.call(this.endpoint.getDataVorlage(vorlage_kurzbz))
.then(response => {
this.formData.body = response.data;
}).catch(this.$fhcAlert.handleSystemError)
.finally(() => {
//this.resetForm();
//closeModal
//closewindwo
});
this.formData.body = response.data.text;
this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError);
},
getPreviewText(){
const data = new FormData();
@@ -197,8 +193,7 @@ export default {
},
handleSelectedVorlage(vorlage_kurzbz) {
if (typeof vorlage_kurzbz === "string") {
this.getVorlagentext(vorlage_kurzbz);
this.formData.subject = vorlage_kurzbz;
this.getDataVorlage(vorlage_kurzbz);
}
},
showPreview(){
@@ -240,7 +235,7 @@ export default {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getVorlagentext(newVal);
return this.getDataVorlage(newVal);
}
}
},
@@ -136,15 +136,13 @@ export default {
}
);
},
getVorlagentext(vorlage_kurzbz){
getDataVorlage(vorlage_kurzbz){
return this.$api
.call(this.endpoint.getVorlagentext(vorlage_kurzbz))
.call(this.endpoint.getDataVorlage(vorlage_kurzbz))
.then(response => {
this.formData.body = response.data;
}).catch(this.$fhcAlert.handleSystemError)
.finally(() => {
//this.resetForm();
});
this.formData.body = response.data.text;
this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError);
},
getPreviewText(id, typeId){
const data = new FormData();
@@ -164,20 +162,11 @@ export default {
insertVariable(selectedItem){
if (this.editor) {
this.editor.insertContent(selectedItem.value + " ");
//TODO(Manu) check: Laden von Variblen geht nicht wenn kein Zeichen danach kommt
// nicht mal mit Punkt adden gehts ohne eintrag nach vars
//this.editor.focus();
// this.editor.setDirty(true);
this.editor.setDirty(true);//seting dirty true if changes appear
// console.log(tinyMCE.activeEditor.isDirty());//dirty output = true
//this.editor.undoManager.add();
//this.editor.insertContent(selectedItem.value + "\u00A0");
//this.editor.insertContent(`<span>${selectedItem.value}&nbsp;</span>`);
//this.editor.selection.setCursorLocation(this.editor.getBody(), 1);
this.editor.fire('input');
this.editor.fire('change');
this.editor.setDirty(true);
this.editor.save();
} else {
console.error("Editor instance is not available.");
@@ -202,8 +191,7 @@ export default {
},
handleSelectedVorlage(vorlage_kurzbz) {
if (typeof vorlage_kurzbz === "string") {
this.getVorlagentext(vorlage_kurzbz);
this.formData.subject = vorlage_kurzbz;
this.getDataVorlage(vorlage_kurzbz);
}
},
hideTemplate(){
@@ -248,7 +236,7 @@ export default {
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getVorlagentext(newVal);
return this.getDataVorlage(newVal);
}
}
},
@@ -274,6 +274,11 @@ export default {
this.$refs.table.reloadTable();
},
buildTreemap(messages) {
if (!messages || !messages.data || messages.data.length === 0)
{
return {data: [], last_page: 0};
}
const last_page = messages.meta.count;
messages = messages.data;
const messageMap = new Map();
@@ -37,16 +37,16 @@ export default {
});
},
loadData(evt) {
if( evt.query.length < 2 )
{
return false;
}
if (evt.query.length < 2)
{
return false;
}
if (this.abortController)
{
this.abortController.abort();
}
if (this.abortController instanceof AbortController
&& this.abortController.signal.aborted === false)
{
this.abortController.abort();
}
this.abortController = new AbortController();
this.$api
@@ -56,16 +56,8 @@ export default {
})
.then(result => {
this.data = result.data;
this.abortController = null;
})
.catch(error => {
if (this.abortController instanceof AbortController
&& this.abortController.signal.aborted === false)
{
this.abortController.abort();
}
this.$fhcAlert.handleSystemError(error);
});
.catch(this.$fhcAlert.handleSystemError);
}
},
template: `
@@ -17,6 +17,7 @@
import CoreSearchbar from "../searchbar/searchbar.js";
import VerticalSplit from "../verticalsplit/verticalsplit.js";
import AppMenu from "../AppMenu.js";
import StvVerband from "./Studentenverwaltung/Verband.js";
import StvList from "./Studentenverwaltung/List.js";
import StvDetails from "./Studentenverwaltung/Details.js";
@@ -32,6 +33,7 @@ export default {
components: {
CoreSearchbar,
VerticalSplit,
AppMenu,
StvVerband,
StvList,
StvDetails,
@@ -345,11 +347,34 @@ export default {
//FHC_JS_DATA_STORAGE_OBJECT.systemerror_mailto = 'ma0068@technikum-wien.at';this.$fhcAlert.handleSystemError(1);
this.handlePersonUrl();
},
template: `
template: /* html */`
<div class="stv">
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-4 col-lg-3 col-xl-2 me-0 px-3" :href="stvRoot">StudVw: {{studiensemesterKurzbz}} {{studiengangKuerzel}}</a>
<button class="navbar-toggler d-md-none m-1 collapsed" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" :aria-label="$p.t('ui/toggle_nav')"><span class="navbar-toggler-icon"></span></button>
<div class="col-md-4 col-lg-3 col-xl-2 d-flex align-items-center">
<button
class="btn btn-outline-light border-0 m-1 collapsed"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#appMenu"
aria-controls="appMenu"
aria-expanded="false"
:aria-label="$p.t('ui/toggle_nav')"
>
<span class="svg-icon svg-icon-apps"></span>
</button>
<a class="navbar-brand me-0" :href="stvRoot">StudVw: {{studiensemesterKurzbz}} {{studiengangKuerzel}}</a>
</div>
<button
class="btn btn-outline-light border-0 d-md-none m-1 collapsed"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#sidebarMenu"
aria-controls="sidebarMenu"
aria-expanded="false"
:aria-label="$p.t('ui/toggle_nav')"
>
<span class="fa-solid fa-table-list"></span>
</button>
<core-searchbar
:searchoptions="searchbaroptions"
:searchfunction="searchfunction"
@@ -358,6 +383,15 @@ export default {
</header>
<div class="container-fluid overflow-hidden">
<div class="row h-100">
<aside id="appMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<div class="offcanvas-header">
StudVw: {{studiensemesterKurzbz}} {{studiengangKuerzel}}
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<div class="offcanvas-body">
<app-menu app-identifier="stv" />
</div>
</aside>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
@@ -31,10 +31,14 @@ export default {
if (this.students.length == 1) {
const student = this.students[0];
if (student.uid)
return Object.fromEntries(Object.entries(this.configStudent).filter(([key, value]) => !value.showOnlyWithoutUid));
return Object.fromEntries(Object.entries(this.configStudent).filter(([key, value]) => !value.showOnlyWithUid));
return Object.fromEntries(Object.entries(this.configStudent).filter(([ , value ]) => !value.showOnlyWithoutUid));
return Object.fromEntries(Object.entries(this.configStudent).filter(([ , value ]) => !value.showOnlyWithUid));
} else if (this.students.every(student => student.uid)) {
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithoutUid));
} else if (this.students.every(student => !student.uid)) {
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid));
}
return this.configStudents;
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid));
}
},
methods: {
@@ -58,11 +62,11 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
template: `
<div class="stv-details h-100 pb-3 d-flex flex-column">
<div class="stv-details h-100 d-flex flex-column">
<div v-if="!students?.length" class="justify-content-center d-flex h-100 align-items-center">
{{$p.t('ui', 'chooseStudent')}}
</div>
<div v-else-if="configStudent && configStudents" class="d-flex flex-column h-100 pb-3">
<div v-else-if="configStudent && configStudents" class="d-flex flex-column h-100">
<fhc-header
:headerData="students"
typeHeader="student"
@@ -7,7 +7,6 @@ import AbschlusspruefungDropdown from "./AbschlusspruefungDropdown.js";
import ApiStudiengang from '../../../../../api/factory/studiengang.js';
import ApiStvAbschlusspruefung from '../../../../../api/factory/stv/abschlusspruefung.js';
import ApiStvAddress from "../../../../../api/factory/stv/kontakt/address.js";
export default {
components: {
@@ -260,14 +259,16 @@ export default {
arrAkadGrad: [],
arrNoten: [],
selectedVorsitz: null,
listeFilteredMitarbeiter: [],
listeAllMitarbeiter: [],
listeAllPersons: [],
filteredMitarbeiter: [],
filteredPersons: [],
selectedPruefer1: null,
selectedPruefer2: null,
selectedPruefer3: null,
listeFilteredPersons: [],
stgInfo: { typ: '', oe_kurzbz: '' }
stgInfo: { typ: '', oe_kurzbz: '' },
abortController: {
mitarbeiter: null,
persons: null
},
}
},
watch: {
@@ -307,23 +308,39 @@ export default {
actionEditAbschlusspruefung(abschlusspruefung_id) {
this.resetForm();
this.statusNew = false;
this.loadAbschlusspruefung(abschlusspruefung_id).then(() => {
this.loadAbschlusspruefung(abschlusspruefung_id).then((result) => {
//set selectedData to enable viewing label in primevue autocomplete fields
this.selectedVorsitz = this.listeAllMitarbeiter.find(
item => item.mitarbeiter_uid === this.formData.vorsitz
);
this.selectedPruefer1 = this.listeAllPersons.find(
item => item.person_id === this.formData.pruefer1
);
this.selectedPruefer2= this.listeAllPersons.find(
item => item.person_id === this.formData.pruefer2
);
this.selectedPruefer3= this.listeAllPersons.find(
item => item.person_id === this.formData.pruefer3
);
const data = result.data;
this.selectedVorsitz = {
label: this.getPersonLabel(data.pv_titelpre, data.pv_nachname, data.pv_vorname, data.pv_titelpost, data.pv_uid),
person_id: data.pv_person_id,
mitarbeiter_uid: data.pv_uid
};
if (data.p1_person_id) {
this.selectedPruefer1 = {
label: this.getPersonLabel(data.p1_titelpre, data.p1_nachname, data.p1_vorname, data.p1_titelpost),
person_id: data.p1_person_id
};
}
if (data.p2_person_id) {
this.selectedPruefer2 = {
label: this.getPersonLabel(data.p2_titelpre, data.p2_nachname, data.p2_vorname, data.p2_titelpost),
person_id: data.p2_person_id
}
};
if (data.p3_person_id) {
this.selectedPruefer3= {
label: this.getPersonLabel(data.p3_titelpre, data.p3_nachname, data.p3_vorname, data.p3_titelpost),
person_id: data.p3_person_id
};
}
});
this.$refs.finalexamModal.show();
},
getPersonLabel(titelpre, nachname, vorname, titelpost, uid) {
return nachname + ' ' + vorname + (titelpre ? ' ' + titelpre : '') + (titelpost ? ' ' + titelpost : '') + (uid ? ' (' + uid + ')' : '');
},
actionDeleteAbschlusspruefung(abschlusspruefung_id) {
this.$fhcAlert
.confirmDelete()
@@ -439,22 +456,61 @@ export default {
printDocument(link) {
window.open(link, '_blank');
},
filterMitarbeiter(event){
const query = event?.query?.toLowerCase()?.trim() || "";
searchMitarbeiter(event) {
if (this.abortController.mitarbeiter) {
this.abortController.mitarbeiter.abort();
}
this.listeFilteredMitarbeiter = this.listeAllMitarbeiter.filter(item => {
const label = (item.label || "").toLowerCase();
return label.includes(query);
});
this.abortController.mitarbeiter = new AbortController();
return this.$api
.call(ApiStvAbschlusspruefung.getMitarbeiter(event.query))
.then(result => {
this.filteredMitarbeiter = [];
for (let mitarbeiter of result.data.retval) {
this.filteredMitarbeiter.push(
{
label: this.getPersonLabel(
mitarbeiter.titelpre,
mitarbeiter.nachname,
mitarbeiter.vorname,
mitarbeiter.titelpost,
mitarbeiter.mitarbeiter_uid
),
person_id: mitarbeiter.person_id,
mitarbeiter_uid: mitarbeiter.mitarbeiter_uid
}
);
}
});
},
filterPersons(event){
const query = event?.query?.toLowerCase()?.trim() || "";
searchPerson(event) {
if (this.abortController.persons) {
this.abortController.persons.abort();
}
this.listeFilteredPersons = this.listeAllPersons.filter(item => {
const label = (item.label || "").toLowerCase();
return label.includes(query);
});
}
this.abortController.persons = new AbortController();
return this.$api
.call(ApiStvAbschlusspruefung.getPruefer(event.query))
.then(result => {
this.filteredPersons = [];
for (let person of result.data.retval) {
this.filteredPersons.push(
{
label: this.getPersonLabel(
person.titelpre,
person.nachname,
person.vorname,
person.titelpost,
person.person_uid
),
person_id: person.person_id
}
);
}
});
},
},
created() {
this.$api
@@ -492,20 +548,6 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getAllMitarbeiter())
.then(result => {
this.listeAllMitarbeiter = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getAllPersons())
.then(result => {
this.listeAllPersons = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
if (!this.student.length) {
this.$api
.call(ApiStudiengang.getStudiengangByKz(this.student.studiengang_kz))
@@ -623,8 +665,8 @@ export default {
optionValue="mitarbeiter_uid"
dropdown
forceSelection
:suggestions="listeFilteredMitarbeiter"
@complete="filterMitarbeiter"
:suggestions="filteredMitarbeiter"
@complete="searchMitarbeiter"
:min-length="3"
>
</form-input>
@@ -636,9 +678,10 @@ export default {
v-model="selectedPruefer1"
optionLabel="label"
optionValue="person_id"
dropdown
forceSelection
:suggestions="listeFilteredPersons"
@complete="filterPersons"
:suggestions="filteredPersons"
@complete="searchPerson"
:min-length="3"
>
</form-input>
@@ -669,9 +712,10 @@ export default {
v-model="selectedPruefer2"
optionLabel="label"
optionValue="person_id"
dropdown
forceSelection
:suggestions="listeFilteredPersons"
@complete="filterPersons"
:suggestions="filteredPersons"
@complete="searchPerson"
:min-length="3"
>
</form-input>
@@ -701,9 +745,10 @@ export default {
v-model="selectedPruefer3"
optionLabel="label"
optionValue="person_id"
dropdown
forceSelection
:suggestions="listeFilteredPersons"
@complete="filterPersons"
:suggestions="filteredPersons"
@complete="searchPerson"
:min-length="3"
>
</form-input>
@@ -3,18 +3,29 @@ import FormInput from "../../../Form/Input.js";
import AkteEdit from "./Archiv/Edit.js";
import ApiStvArchiv from '../../../../api/factory/stv/archiv.js';
import ApiStvDocuments from '../../../../api/factory/stv/documents.js';
import DocumentDropdown from "../Details/Archiv/DocumentDropdown.js";
export default {
name: 'Archiv',
components: {
CoreFilterCmpt,
FormInput,
AkteEdit
AkteEdit,
DocumentDropdown
},
inject: {
currentSemester: {
from: 'currentSemester'
}
},
/* isBerechtigtDocAndOdt: {
from: 'hasPermissionOutputformat',
default: false
},*/
cisRoot: {
from: 'cisRoot'
},
},
props: {
modelValue: Object,
@@ -64,7 +75,8 @@ export default {
'abschlussdokument_lehrgaenge.xml.php': [
'AbschlussdokumentLehrgaenge'
]
}
},
documentDropdownObject: {}
};
},
computed: {
@@ -184,6 +196,38 @@ export default {
];
return events;
},
studentUids() {
if (this.modelValue.uid)
{
return [this.modelValue.uid];
}
return this.modelValue.map(e => e.uid);
},
studentKzs(){
if (this.modelValue.uid)
{
return [this.modelValue.studiengang_kz];
}
return this.modelValue.map(e => e.studiengang_kz);
},
stg_kz(){
return this.studentKzs[0];
},
showAllFormats() {
if( this.isBerechtigtDocAndOdt === false
|| !Array.isArray(this.isBerechtigtDocAndOdt) )
{
return false;
}
let retval = this.isBerechtigtDocAndOdt.includes(this.stgInfo.oe_kurzbz);
return retval;
},
showDropDownMulti(){
if (this.modelValue.length) {
return true;
}
return false;
}
},
watch: {
@@ -252,14 +296,43 @@ export default {
}
},
created() {
this.$api
.call(ApiStvArchiv.getArchivVorlagen())
.then(result => {
this.vorlagenArchiv = result.data;
this.selectedVorlage = result.data.filter(o => o.vorlage_kurzbz == 'Zeugnis')[0];
})
.catch(this.$fhcAlert.handleSystemError);
if (this.modelValue.length) {
const params = {
studiensemester_kurzbz: this.currentSemester,
studiengang_kz: this.stg_kz
};
this.$api
.call(ApiStvArchiv.getArchivVorlagen())
.then(result => {this.vorlagenArchiv = result.data; this.selectedVorlage = result.data.filter(o => o.vorlage_kurzbz == 'Zeugnis')[0];})
.call(ApiStvDocuments.getDocumentDropdownMulti(this.studentUids, params))
.then(result => {
this.documentDropdownObject = result;
})
.catch(this.$fhcAlert.handleSystemError);
} else {
const params = {
prestudent_id: this.modelValue.prestudent_id,
studiensemester_kurzbz: this.currentSemester,
studiengang_kz: this.modelValue.studiengang_kz
};
this.$api
.call(ApiStvDocuments.getDocumentDropdown(params))
.then(result => {
this.documentDropdownObject = result;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
template: `
<div class="stv-details-archiv h-100 d-flex flex-column">
<core-filter-cmpt
ref="table"
table-only
@@ -270,6 +343,7 @@ export default {
:reload-btn-infotext="this.$p.t('table', 'reload')"
>
<template #actions>
<div class="input-group w-auto">
<select class="form-select" v-model="selectedVorlage">
<option v-for="vorlage in vorlagenArchiv" :key="vorlage.vorlage_kurzbz" :value="vorlage">
@@ -285,6 +359,17 @@ export default {
{{ $p.t('stv/archiv_dokument_archivieren') }}
</button>
</div>
<document-dropdown
v-if="documentDropdownObject.data"
:documents="documentDropdownObject.data"
:showAllFormats='true'
:studentUids="studentUids"
:showDropDownMulti="showDropDownMulti"
:cisRoot="cisRoot"
:stgKz="stg_kz"
></document-dropdown>
</template>
</core-filter-cmpt>
<akte-edit ref="edit" :config="config" @saved="updateData"></akte-edit>
@@ -0,0 +1,99 @@
export default {
name: "DocumentDropdown",
props: {
documents: {
type: [Object, Array],
required: true,
},
studentUids: {
type: [Array, String],
required: true,
default: () => []
},
showDropDownMulti: {
type: Boolean,
required: true
},
cisRoot: {
type: String,
required: true
},
stgKz: {
type: Number,
required: true
},
showAllFormats: {
type: Boolean,
required: true
}
},
data() {
return {};
},
methods: {
printDokument(url, scope){
//TODO Manu(check if logic not in content (Zutrittkarte also in content folder))
let linkToPdf = this.cisRoot + 'content/' + url;
window.open(linkToPdf, '_blank');
}
},
template: `
<div class="stv-document-dropdown btn-group">
<button
ref="toolbarButton"
type="button"
class="btn btn-secondary dropdown-toggle px-5 ms-5"
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
aria-expanded="false"
>
{{this.$p.t('dokumente','dokument_erstellen')}}
</button>
<ul class="dropdown-menu dropdown-menu-right">
<template v-for="doc in documents" :key="doc.id">
<li v-if="doc.type === 'documenturl'">
<button class="dropdown-item" type="button" @click="printDokument(doc.url, doc.scope)">
{{ doc.name }}
</button>
</li>
<li v-else-if="doc.type === 'submenu'" class="dropend">
<a
class="dropdown-item dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{{ doc.name }}
</a>
<ul class="dropdown-menu">
<template v-for="child in doc.data" :key="child.id">
<li v-if="child.type === 'documenturl'">
<button class="dropdown-item" type="button" @click="printDokument(child.url, child.scope)">
{{ child.name }}
</button>
</li>
<li v-else-if="child.type === 'submenu'" class="dropend">
<a
class="dropdown-item dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{{ child.name }}
</a>
</li>
</template>
</ul>
</li>
</template>
</ul>
</div>`
};
@@ -0,0 +1,216 @@
import GroupsLvb from './Groups/Lvb.js';
import GroupsSpecial from './Groups/Special.js';
import GroupsList from './Groups/List.js';
import ApiStvGroups from '../../../../api/factory/stv/group.js';
import ApiStvDetails from '../../../../api/factory/stv/details.js';
export default {
name: 'TabGroups',
components: {
GroupsLvb,
GroupsSpecial,
GroupsList
},
inject: {
$reloadList: {
from: '$reloadList',
required: true
},
currentSemester: {
form: 'currentSemester',
required: true
}
},
props: {
modelValue: [Object, Array]
},
data() {
return {
hasOrgforms: false,
lvbList: [],
specialGroups: [],
selectedOrgform: false,
selectedSemester: false,
selectedVerband: false,
selectedGruppe: false,
multiFormHandler: (form, errors) => {
function _split_errors(result, [uid, errors]) {
const gruppe_kurzbz = [];
const studiensemester_kurzbz = [];
const others = {};
errors.forEach(error => {
_split_messages(error.messages, gruppe_kurzbz, studiensemester_kurzbz, others);
});
if (gruppe_kurzbz.length) {
if (!result.formFeedback.gruppe_kurzbz)
result.formFeedback.gruppe_kurzbz = [];
result.formFeedback.gruppe_kurzbz.push(...gruppe_kurzbz);
}
if (studiensemester_kurzbz.length) {
if (!result.formFeedback.studiensemester_kurzbz)
result.formFeedback.studiensemester_kurzbz = [];
result.formFeedback.studiensemester_kurzbz.push(...studiensemester_kurzbz);
}
if (Object.keys(others).length) {
result.toast[uid] = [
{ type: 'validation', messages: others }
];
}
return result;
}
function _split_messages(messages, gruppe_kurzbz, studiensemester_kurzbz, others) {
Object.entries(messages).forEach(([field, msg]) => {
if (field == 'gruppe_kurzbz') {
gruppe_kurzbz.push(msg);
} else if (field == 'studiensemester_kurzbz') {
studiensemester_kurzbz.push(msg);
} else {
if (!others[field])
others[field] = [];
others[field].push(msg);
}
});
}
const { formFeedback, toast } = Object.entries(errors)
.reduce(_split_errors, { formFeedback: {}, toast: {} });
if (formFeedback.gruppe_kurzbz)
formFeedback.gruppe_kurzbz = formFeedback.gruppe_kurzbz
.filter((v,k,a) => a.indexOf(v) == k);
if (formFeedback.studiensemester_kurzbz)
formFeedback.studiensemester_kurzbz = formFeedback.studiensemester_kurzbz
.filter((v,k,a) => a.indexOf(v) == k);
form.clearValidation();
if (Object.keys(formFeedback)) {
form.setFeedback(false, formFeedback);
}
if (Object.keys(toast).length) {
console.log(toast);
this.$api.getErrorHandler().handler.toast(toast);
}
}
};
},
computed: {
allAreStudents() {
if (Array.isArray(this.modelValue))
return this.modelValue.every(ps => ps.uid);
return this.modelValue.uid;
},
sharedStg() {
if (Array.isArray(this.modelValue)) {
const first = this.modelValue.find(Boolean);
if (this.modelValue.every(ps => ps.studiengang_kz === first.studiengang_kz))
return first.studiengang_kz;
return undefined;
}
return this.modelValue.studiengang_kz;
}
},
methods: {
showNewGroupModal() {
this.$refs.newGroupModal.show()
},
changeLvb(params) {
let data = { semester: params.semester };
if (params.verband && params.verband != " ") {
data.verband = params.verband;
if (params.gruppe && params.gruppe != " ")
data.gruppe = params.gruppe;
}
let endpoint;
if (Array.isArray(this.modelValue)) {
endpoint = this.modelValue.map(student => [
student.uid + ' (' + student.vorname + ' ' + student.nachname + ')',
ApiStvDetails.save(
student.prestudent_id,
this.currentSemester,
data
)
]);
} else {
endpoint = ApiStvDetails.save(
this.modelValue.prestudent_id,
this.currentSemester,
data
);
}
this.$api
.call(endpoint)
.then(() => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.$reloadList();
})
.catch(this.$fhcAlert.handleSystemError);
},
addSpecialGroup() {
const gruppe_kurzbz = this.$refs.newGroupModal.value.gruppe_kurzbz || this.$refs.newGroupModal.value;
if (Array.isArray(this.modelValue)) {
this.$refs.newGroupModal.$refs.form
.call(
this.modelValue.map(student => [
student.uid + ' (' + student.vorname + ' ' + student.nachname + ')',
ApiStvGroups.add(
student.uid,
gruppe_kurzbz,
this.currentSemester
)
]),
{
errorHandling: {
combine: { form: ['validation'] },
handler: { form: this.multiFormHandler }
}
}
)
.then(result => {
const successes = result.filter(res => res.status == 'fulfilled');
if (result.length == successes.length) {
this.$refs.newGroupModal.hide();
}
if (successes.length) {
this.$fhcAlert.alertSuccess(this.$p.t('gruppenmanagement/groups_added', { n: successes.length }));
this.$refs.list.reload();
}
})
.catch(this.$fhcAlert.handleSystemError);
} else {
this.$refs.newGroupModal.$refs.form
.call(ApiStvGroups.add(this.modelValue.uid, gruppe_kurzbz, this.currentSemester))
.then(() => {
this.$refs.newGroupModal.hide();
this.$fhcAlert.alertSuccess(this.$p.t('gruppenmanagement/groups_added', { n: 1 }));
this.$refs.list.reload();
})
.catch(this.$fhcAlert.handleSystemError);
}
}
},
template: /* html */`
<div class="stv-details-groups h-100 d-flex flex-column">
<h3>{{ $p.t('gruppenmanagement/special_groups') }}</h3>
<groups-special
ref="newGroupModal"
:default-stg="sharedStg"
@submit.capture.prevent="addSpecialGroup"
/>
<groups-list
ref="list"
class="mb-3"
:students="modelValue"
@new="showNewGroupModal"
/>
<h3>{{ $p.t('lehre/lehrverband') }}</h3>
<groups-lvb
:students="modelValue"
@submit="changeLvb"
/>
</div>`
};
@@ -0,0 +1,171 @@
import { CoreFilterCmpt } from "../../../../filter/Filter.js";
import ApiStvGroups from '../../../../../api/factory/stv/group.js';
export default {
name: 'TabGroupsList',
components: {
CoreFilterCmpt
},
inject: [
"currentSemester"
],
props: {
students: Object
},
emits: [
"new"
],
data() {
return {
phrasenLoaded: false,
optionsReady: true
};
},
computed: {
tabulatorOptions() {
let ajaxRequestFunc, ajaxResponse, initialFilter;
if (Array.isArray(this.students)) {
ajaxRequestFunc = () => {
return this.$api.call(
this.students.map(student => ApiStvGroups.getGruppen(student.uid))
);
};
ajaxResponse = (url, params, response) => {
return response.reduce((data, result) => [
...data,
...result.value.data
], []);
};
initialFilter = [
this.students.map(student => {
return { field: "uid", type: "=", value: student.uid };
}),
[
{ field: "studiensemester_kurzbz", type: "=", value: null },
{ field: "studiensemester_kurzbz", type: "=", value: this.currentSemester }
]
];
} else {
ajaxRequestFunc = () => {
return this.$api.call(
ApiStvGroups.getGruppen(this.students.uid)
);
};
ajaxResponse = (url, params, response) => {
return response.data;
};
initialFilter = [
{ field: "uid", type: "=", value: this.students.uid },
[
{ field: "studiensemester_kurzbz", type: "=", value: null },
{ field: "studiensemester_kurzbz", type: "=", value: this.currentSemester }
]
];
}
return {
ajaxURL: 'dummy',
ajaxRequestFunc,
ajaxResponse,
initialFilter,
columns: [
{ title: this.$p.t('gruppenmanagement/gruppe'), field: "gruppe_kurzbz" },
{ title: this.$p.t('ui/bezeichnung'), field: "bezeichnung" },
{ title: this.$p.t('lehre/studiensemester'), field: "studiensemester_kurzbz" },
{
title: this.$p.t('gruppenmanagement/automatisch_generiert'),
field: "generiert",
formatter: "tickCross",
hozAlign: "center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
}
},
{ title: this.$p.t('ui/student_uid'), field: "uid" },
{
title: this.$p.t('global/actions'),
field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
formatter: (cell, formatterParams, onRendered) => {
const container = document.createElement('div');
container.className = "d-flex gap-2";
const data = cell.getData();
const button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', () =>
this.actionDeleteGroup(data)
);
if (data.generiert)
button.disabled = true;
container.append(button);
return container;
},
frozen: true
}
],
height: 'auto',
index: 'group_id',
persistenceID: 'stv-details-group-list'
};
}
},
watch: {
tabulatorOptions() {
// Refresh Tabulator if options have changed
this.optionsReady = false;
this.$nextTick(() => this.optionsReady = true);
}
},
methods: {
actionDeleteGroup(data) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? data
: Promise.reject({handled: true}))
.then(this.deleteGroup)
.catch(this.$fhcAlert.handleSystemError);
},
deleteGroup(params) {
return this.$api
.call(ApiStvGroups.deleteGroup(params))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
this.reload();
})
.catch(this.$fhcAlert.handleSystemError);
},
reload() {
this.$refs.table.reloadTable();
}
},
mounted() {
this.$p
.loadCategory(['global', 'lehre', 'ui', 'gruppenmanagement'])
.then(() => {
this.phrasenLoaded = true;
});
},
template: /* html */`
<div class="stv-details-groups-list">
<core-filter-cmpt
v-if="phrasenLoaded && optionsReady"
ref="table"
:tabulator-options="tabulatorOptions"
table-only
:side-menu="false"
reload
:reload-btn-infotext="$p.t('table/reload')"
new-btn-show
:new-btn-label="$p.t('lehre/gruppe')"
@click:new="$emit('new')"
>
</core-filter-cmpt>
</div>`
};
@@ -0,0 +1,241 @@
import FhcForm from "../../../../Form/Form.js";
import ApiStvLvb from '../../../../../api/factory/stv/lehrverband.js';
export default {
name: 'TabGroupsLvb',
components: {
FhcForm
},
props: {
students: Object
},
emits: [
"submit"
],
data() {
return {
lvbList: [],
selectedSemester: false,
selectedVerband: false,
selectedGruppe: false
};
},
computed: {
allAreStudents() {
if (Array.isArray(this.students))
return this.students.every(ps => ps.uid);
return this.students.uid;
},
studiengang_kz() {
if (Array.isArray(this.students)) {
const first = this.students.find(Boolean);
if (this.students.every(ps => ps.studiengang_kz === first.studiengang_kz))
return first.studiengang_kz;
return false;
}
return this.students.studiengang_kz;
},
semester: {
get() {
if (this.selectedSemester !== false) {
if (this.lvbList.some(item => item.semester == this.selectedSemester))
return this.selectedSemester;
return false;
}
if (Array.isArray(this.students)) {
const first = this.students.find(Boolean);
if (this.lvbList.every(item => item.semester != first.semester))
return false;
if (this.students.every(ps => ps.semester === first.semester))
return first.semester;
return false;
}
if (this.lvbList.some(item => item.semester == this.students.semester))
return this.students.semester;
return false;
},
set(value) {
this.selectedSemester = value;
}
},
verband: {
get() {
if (this.semester === false)
return false;
if (this.selectedVerband !== false) {
if (this.lvbListVerband.some(item => item.verband == this.selectedVerband))
return this.selectedVerband;
return false;
}
if (Array.isArray(this.students)) {
const first = this.students.find(Boolean);
if (this.lvbListVerband.every(item => item.verband != first.verband))
return false;
if (this.students.every(ps => ps.verband === first.verband))
return first.verband;
return false;
}
if (this.lvbListVerband.some(item => item.verband == this.students.verband))
return this.students.verband;
return false;
},
set(value) {
this.selectedVerband = value;
}
},
gruppe: {
get() {
if (this.verband === false)
return false;
if (this.selectedGruppe !== false) {
if (this.lvbListGruppe.some(item => item.gruppe == this.selectedGruppe))
return this.selectedGruppe;
return false;
}
if (Array.isArray(this.students)) {
const first = this.students.find(Boolean);
if (this.lvbListGruppe.every(item => item.gruppe != first.gruppe))
return false;
if (this.students.every(ps => ps.gruppe === first.gruppe))
return first.gruppe;
return false;
}
if (this.lvbListGruppe.some(item => item.gruppe == this.students.gruppe))
return this.students.gruppe;
return false;
},
set(value) {
this.selectedGruppe = value;
}
},
stgSemester() {
if (!this.lvbList.length)
return [];
const semester = new Set(this.lvbList.map(lvb => lvb.semester));
return Array.from(semester).sort((a, b) => a - b);
},
lvbListVerband() {
if (!this.lvbList.length)
return [];
if (this.semester === false)
return [];
return this.lvbList.filter(lvb => this.semester == lvb.semester);
},
semesterVerband() {
if (!this.lvbListVerband.length)
return [];
const verband = new Set(this.lvbListVerband.map(lvb => lvb.verband.replace(/ /g, '')));
return Array.from(verband).filter(Boolean).sort();
},
lvbListGruppe() {
if (!this.lvbListVerband.length)
return [];
if (this.verband === false)
return [];
return this.lvbListVerband.filter(lvb => this.verband == lvb.verband);
},
verbandGruppe() {
if (!this.lvbListGruppe.length)
return [];
const gruppe = new Set(this.lvbListGruppe.map(lvb => lvb.gruppe.replace(/ /g, '')));
return Array.from(gruppe).filter(Boolean).sort();
}
},
watch: {
studiengang_kz() {
this.loadGroupsForStg();
}
},
methods: {
loadGroupsForStg() {
this.lvbList = [];
if (this.studiengang_kz === false)
return;
this.$api
.call(ApiStvLvb.getTree(this.studiengang_kz))
.then(result => this.lvbList = result.data)
.catch(this.$fhcAlert.handleSystemError)
},
onSubmit() {
let params = {
studiengang_kz: this.studiengang_kz,
semester: this.semester,
verband: this.verband,
gruppe: this.gruppe
};
this.$emit("submit", params);
}
},
created() {
this.loadGroupsForStg();
},
template: /* html */`
<div class="stv-details-groups-lvb">
<fhc-form
v-if="allAreStudents && studiengang_kz"
ref="form"
class="input-group"
@submit.prevent="onSubmit"
>
<span class="input-group-text">
{{ $p.t('lehre/semester') }}
</span>
<select
v-model="semester"
class="form-select"
>
<option v-for="semester in stgSemester">{{ semester }}</option>
</select>
<span
class="input-group-text"
:class="{'text-muted': semester === false}"
>
{{ $p.t('lehre/verband') }}
</span>
<select
v-model="verband"
class="form-select"
:disabled="semester === false"
>
<option v-for="verband in semesterVerband">{{ verband }}</option>
</select>
<span
class="input-group-text"
:class="{'text-muted': verband === false}"
>
{{ $p.t('lehre/gruppe') }}
</span>
<select
v-model="gruppe"
class="form-select"
:disabled="verband === false"
>
<option v-for="gruppe in verbandGruppe">{{ gruppe }}</option>
</select>
<button
type="submit"
class="btn btn-primary"
:disabled="gruppe === false && verband === false"
>
{{ $p.t('ui/change') }}
</button>
</fhc-form>
<div v-if="!allAreStudents" class="alert alert-danger">
{{ $p.t('stv/groups_error_notallstudents') }}
</div>
<div v-if="!studiengang_kz" class="alert alert-danger">
{{ $p.t('stv/groups_error_notsamestg') }}
</div>
</div>`
};
@@ -0,0 +1,97 @@
import BsModal from "../../../../Bootstrap/Modal.js";
import FhcForm from "../../../../Form/Form.js";
import FormValidation from "../../../../Form/Validation.js";
import FormInput from "../../../../Form/Input.js";
import ApiStvGroups from '../../../../../api/factory/stv/group.js';
export default {
name: 'TabGroupsSpecial',
components: {
BsModal,
FhcForm,
FormValidation,
FormInput,
PvAutocomplete: primevue.autocomplete
},
props: {
defaultStg: Number
},
emits: [
"chosen"
],
data() {
return {
value: '',
groupSuggestions: []
};
},
methods: {
show() {
this.$refs.popup.show();
},
hide() {
this.$refs.popup.hide();
},
getGroupSuggestions({ query }) {
this.$api
.call(ApiStvGroups.search(query, this.defaultStg))
.then(result => this.groupSuggestions = result.data)
.catch(this.$fhcAlert.handleSystemError);
},
onSubmit(evt) {
if (!evt.defaultPrevented) {
evt.preventDefault();
this.hide();
}
},
onEnter() {
/**
* NOTE(chris): PrimeVue: AutoComplete: Enter does not submit form #5618
* @see https://github.com/primefaces/primevue/issues/5618
*
* this is fixed in 3.52.0
* until then this function fill fix it
*/
if (!this.$refs.autocomplete.$refs.input.overlayVisible) {
this.$refs.form.$el.requestSubmit();
}
},
modalOpened() {
this.$refs.autocomplete.$refs.input.$refs.focusInput.focus();
},
modalClosed() {
this.value = '';
this.$refs.form.clearValidation();
}
},
template: /* html */`
<bs-modal
ref="popup"
class="stv-details-groups-special"
@hide-bs-modal="modalClosed"
@shown-bs-modal="modalOpened"
>
<template #title>
{{ $p.t('gruppenmanagement/add_group') }}
</template>
<fhc-form ref="form" @submit="onSubmit">
<form-validation />
<div class="input-group">
<form-input
ref="autocomplete"
type="autocomplete"
name="gruppe_kurzbz"
v-model="value"
container-class="flex-grow-1"
input-class="w-100"
:suggestions="groupSuggestions"
:option-label="el => el.gruppe_kurzbz + (el.bezeichnung ? ' (' + el.bezeichnung + ')' : '')"
@complete="getGroupSuggestions"
@keydown.enter.capture="onEnter"
/>
<button type="submit" class="btn btn-primary">{{ $p.t('ui/hinzufuegen') }}</button>
</div>
</fhc-form>
</bs-modal>`
};
@@ -1,187 +0,0 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import ApiStvGroups from '../../../../../api/factory/stv/group.js';
export default {
name: 'TblGroups',
components: {
CoreFilterCmpt,
},
inject: {
currentSemester: {
from: 'currentSemester',
},
},
props: {
student: Object
},
data() {
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvGroups.getGruppen(this.student.uid)
),
ajaxResponse: (url, params, response) => response.data,
initialFilter: {
logic: "and",
filters: [
{ field: "uid", operator: "eq", value: this.student.uid },
{
logic: "or",
filters: [
{ field: "studiensemester_kurzbz", operator: "eq", value: null },
{ field: "studiensemester_kurzbz", operator: "eq", value: this.currentSemester }
]
}
]
},
columns: [
{title: "Gruppe", field: "gruppe_kurzbz"},
{title: "Bezeichnung", field: "bezeichnung"},
{title: "Semester", field: "studiensemester_kurzbz"},
{
title: "automatisch generiert",
field: "generiert",
formatter: "tickCross",
hozAlign: "center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
}
},
{title: "UID", field: "uid"},
{
title: 'Aktionen', field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
formatter: (cell, formatterParams, onRendered) => {
const container = document.createElement('div');
container.className = "d-flex gap-2";
const data = cell.getData();
const button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', () =>
this.actionDeleteGroup(data.gruppe_kurzbz)
);
if (data.generiert)
button.disabled = true;
container.append(button);
return container;
},
frozen: true
},
],
layout: 'fitDataFill',
height: 'auto',
index: 'group_id',
persistenceID: 'stv-details-gruppe'
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['global', 'person', 'stv', 'ui', 'gruppenmanagement']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('gruppe_kurzbz').component.updateDefinition({
title: this.$p.t('gruppenmanagement', 'gruppe')
});
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('ui', 'bezeichnung')
});
cm.getColumnByField('generiert').component.updateDefinition({
title: this.$p.t('gruppenmanagement', 'automatisch_generiert')
});
cm.getColumnByField('uid').component.updateDefinition({
title: this.$p.t('ui', 'student_uid')
});
//Interference with Filter if not commented out
/*
cm.getColumnByField('studiensemester_kurzbz').component.updateDefinition({
title: this.$p.t('lehre', 'studiensemester')
});*/
}
}
],
}
},
methods: {
actionDeleteGroup(gruppe_kurzbz) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? gruppe_kurzbz
: Promise.reject({handled: true}))
.then(this.deleteGroup)
.catch(this.$fhcAlert.handleSystemError);
},
deleteGroup(gruppe_kurzbz) {
const group_id = {
id: this.student.uid,
gruppe_kurzbz: gruppe_kurzbz
};
return this.$api
.call(ApiStvGroups.deleteGroup(group_id))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
}).catch(this.$fhcAlert.handleSystemError)
.finally(() => {
window.scrollTo(0, 0);
this.reload();
});
},
reload() {
this.$refs.table.reloadTable();
},
},
watch: {
currentSemester(newVal) {
if (newVal) {
this.$refs.table.tabulator.clearFilter(); // Clear old filters
this.$refs.table.tabulator.setFilter((data) => {
return (
data.uid === this.student.uid &&
(
data.studiensemester_kurzbz === newVal ||
data.studiensemester_kurzbz === null
)
);
});
}
},
student() {
this.$refs.table.reloadTable();
}
},
template: `
<div class="stv-details-gruppen h-100 pb-3">
<h5>{{$p.t('stv', 'tab_groups')}}</h5>
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
reload
:reload-btn-infotext="this.$p.t('table', 'reload')"
>
</core-filter-cmpt>
</div>
`
}
@@ -0,0 +1,101 @@
export default {
name: "Kontaktieren",
computed: {
internMails() {
if (this.modelValue.mail_intern)
{
return [this.modelValue.mail_intern];
}
return this.modelValue.map(e => e.mail_intern);
},
privateMails()
{
if (this.modelValue.mail_privat)
{
return [this.modelValue.mail_privat];
}
return this.modelValue.map(e => e.mail_privat);
},
},
props: {
modelValue: Object
},
methods: {
async splitMails(mails, event) {
let splititem = ",";
let maillist = mails.join(splititem);
let mailto = "";
if (maillist.length > 2024)
{
if (await this.$fhcAlert.confirm({message: this.$p.t('stv', 'zuvieleEMails') }) === false)
return;
}
let firstrun = true;
let useBcc = event?.ctrlKey || event?.metaKey;
while (maillist.length > 0)
{
if (maillist.length > 2024)
{
let splitposition = maillist.lastIndexOf(splititem, 1900);
mailto = maillist.substring(0, splitposition);
maillist = maillist.substring(splitposition + 1);
}
else
{
mailto = maillist;
maillist = "";
}
let mailLink = useBcc ? `mailto:?bcc=${mailto}` : `mailto:${mailto}`;
if (firstrun)
{
window.location.href = mailLink;
firstrun = false;
}
else
{
if (await this.$fhcAlert.confirm({message: this.$p.t('stv', 'weitereEMail')}) === true)
{
window.location.href = mailLink;
}
}
}
},
internMail(event) {
if (this.internMails.length)
{
this.splitMails(this.internMails, event);
}
},
privateMail(event) {
if (this.privateMails.length)
{
this.splitMails(this.privateMails, event);
}
}
},
template: `
<div>
<div id="elementID"></div>
<div class="row">
<div class="col-lg-2">
<button class="btn btn-primary mb-2" @click="internMail($event)" :title="$p.t('stv', 'bccEMail')">
<i class="fa-solid fa-mail"></i> {{$p.t('stv', 'internEMail')}}
</button>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<button class="btn btn-primary mb-2" @click="privateMail($event)" :title="$p.t('stv', 'bccEMail')">
<i class="fa-solid fa-mail"></i> {{$p.t('stv', 'privateEMail')}}
</button>
</div>
</div>
</div>`
};
@@ -401,7 +401,7 @@ export default{
this.$api
.call(ApiStvPrestudent.getLastBismeldestichtag())
.then(result => {
this.dataMeldestichtag = result.data[0].meldestichtag;
this.dataMeldestichtag = result.data[0]?.meldestichtag;
if (this.$refs.table && this.$refs.table.tableBuilt)
this.$refs.table.tabulator.redraw(true);
})
@@ -0,0 +1,24 @@
import Projektarbeit from './Projektarbeit/Projektarbeit.js';
export default {
name: "TabProjektarbeit",
components: {
Projektarbeit
},
provide() {
return {
config: this.config
};
},
props: {
modelValue: Object,
config: Object
},
data(){
return {}
},
template: `
<div class="stv-details-projektarbeit h-100 d-flex flex-column">
<projektarbeit ref="projektarbeit" :student="modelValue"></projektarbeit>
</div>`
};
@@ -0,0 +1,411 @@
import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js';
export default {
components: {
FormForm,
FormInput,
PvAutoComplete
},
emits: ['projekttypChanged'],
inject: {
defaultSemester: {
from: 'defaultSemester'
}
},
computed: {
// prepared Lehreinheiten (with compound Bezeichnung)
arrLes() {
let lehreinheiten = [];
if (this.formData.lehrveranstaltung_id) {
let lv = this.arrLvs.find(lv => {return lv.lehrveranstaltung_id == this.formData.lehrveranstaltung_id});
if (lv) lehreinheiten = lv.lehreinheiten
}
for (let le of lehreinheiten)
{
let bezeichnung = le.lehrfach_kurzbz + '-' + le.lehrform_kurzbz + ' ' + le.lehrfach_bezeichnung + ' ';
for (let grp of le.lehreinheitgruppen)
{
bezeichnung += grp.gruppe_kurzbz ? grp.gruppe_kurzbz : '' + grp.semester ?? '' + grp.verband ?? '' + grp.gruppe ?? '';
}
bezeichnung += ' (' + le.lektoren.join(' ') + ') ID: ' + le.lehreinheit_id;
le.bezeichnung = bezeichnung;
}
return lehreinheiten;
}
},
props: {
statusNew: Boolean,
student: Object,
projektarbeit: Object
},
data() {
return {
formData: {
projektarbeit_id: null,
titel: null,
titel_english: null,
themenbereich: null,
projekttyp_kurzbz: null,
firma: null,
lehrveranstaltung_id: null,
lehreinheit_id: null,
beginn: null,
ende: null,
freigegeben: true,
gesperrtbis: null,
note: null,
final: true,
anmerkung: null
},
arrTypen: [],
arrFirmen: [],
arrLvs: [],
arrNoten: [],
filteredFirmen: [],
abortController: {
firma: null
}
}
},
watch: {
'formData.projekttyp_kurzbz'(newValue, oldValue) {
this.$emit('projekttypChanged', newValue);
}
},
methods: {
resetForm() {
this.formData.projektarbeit_id = null;
this.formData.titel = null;
this.formData.titel_english = null;
this.formData.themenbereich = null;
this.formData.projekttyp_kurzbz = null;
this.formData.firma = null;
this.formData.lehrveranstaltung_id = null;
this.formData.lehreinheit_id = null;
this.formData.beginn = null;
this.formData.ende = null;
this.formData.freigegeben = true;
this.formData.gesperrtbis = null;
this.formData.note = null;
this.formData.final = true;
this.formData.anmerkung = null;
this.$refs.formDetails.clearValidation();
},
getFormData(statusNew, studiensemester_kurzbz, additional_lehrveranstaltung_id) {
this.$api
.call(ApiStvProjektarbeit.getTypenProjektarbeit())
.then(result => {
this.arrTypen = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvProjektarbeit.getLehrveranstaltungen(
this.student.uid,
statusNew ? this.student.studiengang_kz : null,
studiensemester_kurzbz ?? this.defaultSemester,
additional_lehrveranstaltung_id
))
.then(result => {
this.arrLvs = result.data
}
)
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvProjektarbeit.getNoten())
.then(result => {
this.arrNoten = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
if (statusNew) this.resetForm();
},
loadProjektarbeit(projektarbeit_id) {
if (this?.formData?.projektarbeit_id == projektarbeit_id) return;
this.resetForm();
return this.$api
.call(ApiStvProjektarbeit.loadProjektarbeit(projektarbeit_id))
.then(result => {
this.formData = result.data;
if (this.formData.firma_id) this.formData.firma = {firma_id: this.formData.firma_id, name: this.formData.firma_name};
return result;
})
.catch(this.$fhcAlert.handleSystemError)
},
addNewProjektarbeit() {
let dataToSend = {
uid: this.student.uid,
formData: this.getPreparedFormData()
};
return this.$refs.formDetails
.call(ApiStvProjektarbeit.addNewProjektarbeit(dataToSend));
},
updateProjektarbeit() {
let dataToSend = {
projektarbeit_id: this.formData.projektarbeit_id,
formData: this.getPreparedFormData()
};
return this.$refs.formDetails
.call(ApiStvProjektarbeit.updateProjektarbeit(dataToSend));
},
searchFirma(event) {
if (this.abortController.firma) {
this.abortController.firma.abort();
}
this.abortController.firma = new AbortController();
return this.$api
.call(ApiStvProjektarbeit.getFirmen(event.query))
.then(result => {
this.filteredFirmen = result.data;
});
},
lvChanged(event) {
this.formData.lehreinheit_id = null;
},
// enrich and modify data before sending
getPreparedFormData() {
let preparedFormData = JSON.parse(JSON.stringify(this.formData)); // deep copy
// set firma Id
if (preparedFormData.firma)
preparedFormData.firma_id = preparedFormData.firma.firma_id;
else
preparedFormData.firma_id = null;
// delete "helper" fields
if (preparedFormData.projektarbeit_id == null) delete(preparedFormData.projektarbeit_id);
delete(preparedFormData.firma);
delete(preparedFormData.firma_name);
delete(preparedFormData.lehrveranstaltung_id);
return preparedFormData;
}
},
template: `
<form-form v-if="!this.student.length" ref="formDetails" @submit.prevent>
<legend>Details</legend>
<p v-if="statusNew">[{{$p.t('ui', 'neu')}}]</p>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-titel"
type="text"
name="titel"
:label="$p.t('projektarbeit', 'titel')"
v-model="formData.titel">
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-titel_english"
type="text"
name="titel_english"
:label="$p.t('projektarbeit', 'titelEnglisch')"
v-model="formData.titel_english"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-themenbereich"
type="text"
name="themenbereich"
:label="$p.t('projektarbeit', 'themenbereich')"
v-model="formData.themenbereich"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-typ"
:label="$p.t('projektarbeit', 'typ')"
type="select"
v-model="formData.projekttyp_kurzbz"
name="projekttyp_kurzbz"
>
<option
v-for="typ in arrTypen"
:key="typ.projekttyp_kurzbz"
:value="typ.projekttyp_kurzbz"
>
{{typ.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-firma"
:label="$p.t('projektarbeit', 'firma')"
type="autocomplete"
optionLabel="name"
v-model="formData.firma"
name="firma"
:suggestions="filteredFirmen"
@complete="searchFirma"
:min-length="3"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-lv"
:label="$p.t('projektarbeit', 'lehrveranstaltung')"
type="select"
v-model="formData.lehrveranstaltung_id"
name="lehrveranstaltung_id"
@change="lvChanged($event)"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="lv in arrLvs"
:key="lv.lehrveranstaltung_id"
:value="lv.lehrveranstaltung_id"
>
{{lv.bezeichnung + (lv.orgform_kurzbz ? ' ' + lv.orgform_kurzbz : '') + ' (' + lv.semester + ' Sem) ID: ' + lv.lehrveranstaltung_id}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-le"
:label="$p.t('projektarbeit', 'lvTeil')"
type="select"
v-model="formData.lehreinheit_id"
name="lehreinheit_id"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="le in arrLes"
:key="le.lehreinheit_id"
:value="le.lehreinheit_id"
>
{{le.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-projektarbeit-beginn"
:label="$p.t('projektarbeit', 'beginn')"
type="DatePicker"
v-model="formData.beginn"
auto-apply
:enable-time-picker="false"
text-input
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
name="beginn"
>
</form-input>
<form-input
container-class="col-6 stv-details-projektarbeit-ende"
:label="$p.t('projektarbeit', 'ende')"
type="DatePicker"
v-model="formData.ende"
auto-apply
:enable-time-picker="false"
text-input
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
name="ende"
>
</form-input>
</div>
<div class="row mb-3 align-items-center">
<form-input
container-class="col-8 stv-details-projektarbeit-gesperrtbis"
:label="$p.t('projektarbeit', 'gesperrtBis')"
type="DatePicker"
v-model="formData.gesperrtbis"
auto-apply
:enable-time-picker="false"
text-input
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
name="gesperrtbis"
>
</form-input>
<div class="col-4">
<form-input
container-class="form-check stv-details-projektarbeit-freigegeben"
type="checkbox"
name="freigegeben"
:label="$p.t('projektarbeit','freigegeben')"
v-model="formData.freigegeben"
>
</form-input>
</div>
</div>
<div class="row mb-3 align-items-center">
<form-input
container-class="col-8 stv-details-projektarbeit-note"
:label="$p.t('projektarbeit', 'note')"
type="select"
v-model="formData.note"
name="note"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="note in arrNoten"
:key="note.note"
:value="note.note"
>
{{note.bezeichnung}}
</option>
</form-input>
<div class="col-4">
<form-input
container-class="form-check stv-details-projektarbeit-final"
type="checkbox"
name="final"
label="final"
v-model="formData.final"
>
</form-input>
</div>
</div>
<div class="row mb-3">
<form-input
container-class="col-12 stv-details-abschlusspruefung-anmerkung"
:label="$p.t('projektarbeit', 'anmerkung')"
type="textarea"
v-model="formData.anmerkung"
name="anmerkung"
:rows= 3
>
</form-input>
</div>
</form-form>`
}
@@ -0,0 +1,407 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import BsModal from "../../../../Bootstrap/Modal.js";
import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js';
import ProjektarbeitDetails from "./Details.js";
import Projektbetreuer from "./Projektbetreuer.js";
export default {
components: {
CoreFilterCmpt,
BsModal,
FormForm,
FormInput,
PvAutoComplete,
ProjektarbeitDetails,
Projektbetreuer
},
inject: {
cisRoot: {
from: 'cisRoot'
},
config: {
from: 'config',
required: true
},
$reloadList: {
from: '$reloadList',
required: true
}
},
props: {
student: Object
},
data() {
return {
tabulatorEvents: [
{
event: 'dataLoaded',
handler: data => this.tabulatorData = data.map(item => {
item.actionDiv = document.createElement('div');
return item;
}),
},
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'ui', 'projektarbeit']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('projekttyp_kurzbz').component.updateDefinition({
title: this.$p.t('projektarbeit', 'typ')
});
cm.getColumnByField('titel').component.updateDefinition({
title: this.$p.t('projektarbeit', 'titel')
});
cm.getColumnByField('beginn').component.updateDefinition({
title: this.$p.t('projektarbeit', 'beginn')
});
cm.getColumnByField('ende').component.updateDefinition({
title: this.$p.t('projektarbeit', 'ende')
});
cm.getColumnByField('freigegeben').component.updateDefinition({
title: this.$p.t('projektarbeit', 'freigegeben')
});
cm.getColumnByField('gesperrtbis').component.updateDefinition({
title: this.$p.t('projektarbeit', 'gesperrtBis')
});
cm.getColumnByField('themenbereich').component.updateDefinition({
title: this.$p.t('projektarbeit', 'themenbereich')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('projektarbeit', 'anmerkung')
});
cm.getColumnByField('firma_id').component.updateDefinition({
title: this.$p.t('projektarbeit', 'firmaId')
});
}
},
],
tabulatorData: [],
editedProjektarbeit: null,
statusNew: true,
studiensemester_kurzbz: null,
lehrveranstaltung_id: null,
activeTab: 'details'
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvProjektarbeit.getProjektarbeit(this.student.uid)),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Projektarbeit ID", field: "projektarbeit_id", visible: false},
{title: "Typ", field: "bezeichnung"},
{title: "Typ Kurzbz", field: "projekttyp_kurzbz", visible: false},
{title: "Studiensemester", field: "studiensemester_kurzbz"},
{title: "Titel", field: "titel"},
{
title: "Abgabe Enduplad",
field: "abgabedatum",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
}
},
{
title: "Beginn",
field: "beginn",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
},
visible: false
},
{
title: "Ende",
field: "ende",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
},
visible: false
},
{
title:"Freigegeben",
field:"freigegeben",
formatter:"tickCross",
hozAlign:"center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
},
visible: false
},
{
title: "Gesperrt bis",
field: "gesperrtbis",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
},
visible: false
},
{title: "Themenbereich", field: "themenbereich", visible: false},
{title: "Anmerkung", field: "anmerkung", visible: false},
{title: "Lehreinheit ID", field: "lehreinheit_id", visible: false},
{title: "Student UID", field: "student_uid", visible: false},
{
title:"Final",
field:"final",
formatter:"tickCross",
hozAlign:"center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
},
visible: false
},
{title: "Firma ID", field: "firma_id", visible: false},
{
title: 'Aktionen', field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
let button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t('ui', 'bearbeiten');
button.addEventListener('click', (event) => {
let data = cell.getData();
this.editedProjektarbeit = data;
this.actionEditProjektarbeit();
});
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-users"></i>';
button.title = this.$p.t('projektarbeit', 'betreuerBearbeiten');
button.addEventListener('click', (event) => {
let data = cell.getData();
this.editedProjektarbeit = data;
this.actionEditBetreuer();
});
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', () =>
this.actionDeleteProjektarbeit(cell.getData().projektarbeit_id)
);
container.append(button);
container.append(cell.getData().actionDiv);
return container;
},
frozen: true
},
],
//layout: 'fitDataFill',
height: 'auto',
minHeight: '200',
selectable: 1,
index: 'projektarbeit_id',
persistence:{
columns: true, //persist column layout
},
persistenceID: 'stv-details-projektarbeit'
}
return options;
}
},
methods: {
actionNewProjektarbeit() {
this.statusNew = true;
this.editedProjektarbeit = null;
this.toggleMenu('details');
this.$refs.projektarbeitDetails.getFormData(this.statusNew);
this.$refs.projektarbeitModal.show();
},
actionEditProjektarbeit() {
this.statusNew = false;
this.toggleMenu('details');
this.$refs.projektarbeitModal.show();
},
actionEditBetreuer() {
this.statusNew = false;
this.toggleMenu('betreuer');
this.$refs.projektarbeitModal.show();
},
actionDeleteProjektarbeit(projektarbeit_id) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? projektarbeit_id
: Promise.reject({handled: true}))
.then(this.deleteProjektarbeit)
.catch(this.$fhcAlert.handleSystemError);
},
addNewProjektarbeit() {
this.$refs.projektarbeitDetails.addNewProjektarbeit()
.then((result) => {
this.projektarbeitSaved();
})
.catch(this.$fhcAlert.handleSystemError);
},
updateProjektarbeit() {
this.$refs.projektarbeitDetails.updateProjektarbeit()
.then((result) => {
this.projektarbeitSaved();
})
.catch(this.$fhcAlert.handleSystemError);
},
deleteProjektarbeit(projektarbeit_id) {
return this.$api
.call(ApiStvProjektarbeit.deleteProjektarbeit(projektarbeit_id))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.reload();
});
},
projektarbeitSaved() {
this.reload();
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.hideModal('projektarbeitModal');
},
setDefaultStunden(projekttyp_kurzbz) {
this.$refs.projektbetreuer.setDefaultStunden(projekttyp_kurzbz);
},
hideModal(modalRef){
this.$refs[modalRef].hide();
},
reload() {
this.$refs.table.reloadTable();
},
toggleMenu(tabId) {
this.activeTab = tabId;
if (this.statusNew == false) {
switch(tabId) {
case 'details':
this.$refs.projektarbeitDetails.getFormData(
this.statusNew, this.editedProjektarbeit?.studiensemester_kurzbz, this.editedProjektarbeit?.lehrveranstaltung_id
);
this.$refs.projektarbeitDetails.loadProjektarbeit(this.editedProjektarbeit?.projektarbeit_id);
break;
case 'betreuer':
this.$refs.projektbetreuer.getFormData(
this.editedProjektarbeit ? this.editedProjektarbeit.projekttyp_kurzbz : null
);
this.$refs.projektbetreuer.getProjektbetreuer(this.editedProjektarbeit?.projektarbeit_id, this.editedProjektarbeit?.studiensemester_kurzbz);
break;
}
}
}
},
template: `
<div class="stv-details-projektarbeit h-100 pb-3">
<h4>{{this.$p.t('stv','tab_projektarbeit')}}</h4>
<core-filter-cmpt
v-if="!this.student.length"
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
reload
new-btn-show
:new-btn-label="this.$p.t('stv', 'tab_projektarbeit')"
@click:new="actionNewProjektarbeit"
>
</core-filter-cmpt>
<!--Modal: projektarbeitModal-->
<bs-modal ref="projektarbeitModal" dialog-class="modal-xl modal-dialog-scrollable" header-class="flex-wrap pb-0">
<template #title>
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('projektarbeit', 'projektarbeitAnlegen')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('projektarbeit', 'projektarbeitBearbeiten')}}</p>
</template>
<template #modal-header-content v-if="!statusNew">
<ul class="nav nav-tabs w-100 mt-3 msg_preview" id="pa_tabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" :class="activeTab == 'details' ? 'active' : ''" id="details-tab" data-bs-toggle="tab" data-bs-target="#details" type="button" role="tab" aria-controls="details" aria-selected="true" @click="toggleMenu('details')">Details</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" :class="activeTab == 'betreuer' ? 'active' : ''" id="betreuer-tab" data-bs-toggle="tab" data-bs-target="#betreuer" type="button" role="tab" aria-controls="betreuer" aria-selected="false" @click="toggleMenu('betreuer')">{{$p.t('projektarbeit', 'betreuerGross')}}</button>
</li>
</ul>
</template>
<div class="tab-content" id="pa_content">
<div class="tab-pane fade show" :class="activeTab == 'details' ? 'active' : ''" id="details" role="tabpanel" aria-labelledby="details-tab">
<div class="row">
<div class="col-12">
<projektarbeit-details ref="projektarbeitDetails" :student="student" @projekttyp-changed="setDefaultStunden">
</projektarbeit-details>
</div>
</div>
</div>
<div class="tab-pane fade show" :class="activeTab == 'betreuer' ? 'active' : ''" id="betreuer" role="tabpanel" aria-labelledby="betreuer-tab">
<div class="row">
<div class="col-12">
<projektbetreuer ref="projektbetreuer" :config="config"></projektbetreuer>
</div>
</div>
</div>
</div>
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="statusNew" class="btn btn-primary" @click="addNewProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
<button v-if="!statusNew && activeTab == 'details'" class="btn btn-primary" @click="updateProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
</div>
`
}
@@ -0,0 +1,561 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import BsModal from "../../../../Bootstrap/Modal.js";
import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
import NewPerson from "../../List/New.js";
import Contact from "../Kontakt/Contact.js";
import Vertrag from "./Vertrag.js";
import ApiStvProjektbetreuer from '../../../../../api/factory/stv/projektbetreuer.js';
export default {
components: {
CoreFilterCmpt,
BsModal,
FormForm,
FormInput,
PvAutoComplete,
NewPerson,
Contact,
Vertrag
},
provide() {
return {
configShowVertragsdetails: this.config.showVertragsdetails
}
},
computed: {
betreuerFormOpened() {
return this.newMode || this.editMode;
}
},
props: {
config: {
type: Object,
default: {}
}
},
data() {
return {
tabulatorOptions: {
columns: [
{title: "Nachname", field: "nachname"},
{title: "Vorname", field: "vorname"},
{title: "Note", field: "note"},
{title: "Punkte", field: "punkte"},
{title: "Stunden", field: "stunden"},
{title: "Stundensatz", field: "stundensatz", visible: false},
{title: "Art", field: "betreuerart_kurzbz", visible: false},
{title: "Person ID", field: "person_id", visible: false},
{title: "Vertrag ID", field: "vertrag_id", visible: false},
{title: "Projektarbeit ID", field: "projektarbeit_id", visible: false},
{
title: 'Aktionen',
field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
let button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t('ui', 'bearbeiten');
button.addEventListener('click', (event) => {
event.stopPropagation();
event.preventDefault();
let data = cell.getData();
this.actionEditProjektbetreuer(data.projektarbeit_id, data.person_id, data.betreuerart_kurzbz);
});
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', (event) => {
event.stopPropagation();
event.preventDefault();
const data = cell.getData();
this.actionDeleteProjektbetreuer(data.betreuer_id, data.projektarbeit_id, data.person_id, data.betreuerart_kurzbz)
});
container.append(button);
let data = cell.getData();
if (data.beurteilungDownloadLink !== null) {
if (data.beurteilungDownloadLink == '') {
button = document.createElement('span');
button.title = this.$p.t('projektarbeit', 'projektarbeitNochNichtBeurteilt')
button.innerHTML = '<button class="btn btn-outline-secondary btn-action" disabled>'+
'<i class="fa-regular fa-file-pdf"></i></button>';
button.addEventListener('click', (event) => {
event.stopPropagation();
event.preventDefault();
});
}
else {
button = document.createElement('a');
button.setAttribute('href', data.beurteilungDownloadLink);
button.setAttribute('role', 'button');
button.innerHTML = '<i class="fa fa-file-pdf"></i>';
button.title = this.$p.t('projektarbeit', 'projektbeurteilungErstellen');
button.className = 'btn btn-outline-secondary btn-action';
button.addEventListener('click', (event) => {
event.stopPropagation();
event.preventDefault();
window.location.href = data.beurteilungDownloadLink;
});
}
container.append(button);
}
return container;
},
frozen: true
},
],
//layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '100',
selectable: true,
selectable: 1,
index: 'betreuer_id',
persistence:{
columns: true, //persist column layout
},
persistenceID: 'stv-details-projektbetreuer'
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'projektarbeit', 'ui']);
// Force layout recalculation for handling overflow text
this.$refs.projektbetreuerTable.tabulator.redraw(true);
}
},
{
event: 'rowSelected',
handler: row => {
let data = row.getData();
this.actionEditProjektbetreuer(data.projektarbeit_id, data.person_id, data.betreuerart_kurzbz);
}
}
],
formData: {
betreuerart_kurzbz: null,
note: null,
stunden: null,
stundensatz: null
},
newMode: false,
editMode: false,
initialFormData: null,
defaultFormDataValues: {stunden: null, stundensatz: null},
projektarbeit_id: null,
editedBetreuerIdx: -1,
arrBetreuerart: [],
arrNoten: [],
filteredBetreuer: [],
autocompleteSelectedBetreuer: null,
beurteilungDownloadLink: null,
vertragFieldsDisabled: false,
abortController: {
betreuer: null
}
}
},
methods: {
actionNewProjektbetreuer() {
this.resetForm();
this.newMode = !this.newMode;
this.editMode = false;
this.captureFormData();
},
actionEditProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) {
this.editMode = true;
this.newMode = false;
this.$api
.call(ApiStvProjektbetreuer.getDefaultStundensaetze(person_id, this.studiensemester_kurzbz))
.then(result => {
this.resetForm();
// get betreuer from tabulator list
let projektbetreuerListe = this.$refs.projektbetreuerTable.tabulator.getData();
const idx = projektbetreuerListe.findIndex(
betr =>
betr.person_id === person_id &&
betr.projektarbeit_id === projektarbeit_id &&
betr.betreuerart_kurzbz === betreuerart_kurzbz
);
if (idx >= 0) { // if betreuer found
// set currently edited betreuer (deep copy)
this.formData = JSON.parse(JSON.stringify(projektbetreuerListe[idx]));
// set download link
if (this.formData.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = this.formData.beurteilungDownloadLink;
// set betreuer for autocomplete field
this.autocompleteSelectedBetreuer = {
person_id: this.formData.person_id,
name: this.formData.name,
vorname: this.formData.vorname,
nachname: this.formData.nachname,
vertrag_id: this.formData.vertrag_id
};
}
// set default stundensatz (if no other is set yet)
if (this.formData.stundensatz == null) this.formData.stundensatz = result.data;
// capture initial form data for detecting changes
this.captureFormData();
})
.catch(this.$fhcAlert.handleSystemError);
},
actionDeleteProjektbetreuer(betreuer_id, projektarbeit_id, person_id, betreuerart_kurzbz) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? {projektarbeit_id, person_id, betreuerart_kurzbz}
: Promise.reject({handled: true}))
.then(result => {
return this.$api
.call(ApiStvProjektbetreuer.deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz))
})
.then(result => {
this.$refs.projektbetreuerTable.tabulator.deleteRow(betreuer_id);
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError);
},
getFormData(projekttyp_kurzbz) {
// default Stundensätze from config
this.defaultFormDataValues.stunden = this.getDefaultStunden(projekttyp_kurzbz);
this.defaultFormDataValues.stundensatz = this.config.defaultProjektbetreuerStundensatz;
// get other initial data
this.$api
.call(ApiStvProjektbetreuer.getBetreuerarten())
.then(result => {
this.arrBetreuerart = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvProjektbetreuer.getNoten())
.then(result => {
this.arrNoten = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
getProjektbetreuer(projektarbeit_id, studiensemester_kurzbz) {
if (projektarbeit_id) {
// if projektarbeit changed, reset the form to hold new data
if (this.projektarbeit_id != projektarbeit_id) {
this.resetForm();
this.resetModes();
}
this.projektarbeit_id = projektarbeit_id;
this.studiensemester_kurzbz = studiensemester_kurzbz;
this.$api
.call(ApiStvProjektbetreuer.getProjektbetreuer(this.projektarbeit_id))
.then(result => {
this.$refs.projektbetreuerTable.tabulator.replaceData(this.addIds(result.data));
})
.catch(this.$fhcAlert.handleSystemError);
} else {
this.emptyBetreuer();
}
},
saveProjektbetreuer() {
this.$refs.formProjektbetreuer.call(
ApiStvProjektbetreuer.saveProjektbetreuer(this.projektarbeit_id, this.getFormDataWithBetreuer())
)
.then(result => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.getProjektbetreuer(this.projektarbeit_id, this.studiensemester_kurzbz);
this.resetModes();
})
.catch(this.$fhcAlert.handleSystemError);
},
searchBetreuer(event) {
if (this.abortController.betreuer) {
this.abortController.betreuer.abort();
}
this.abortController.betreuer = new AbortController();
return this.$api
.call(ApiStvProjektbetreuer.getProjektbetreuerBySearchQuery(event.query))
.then(result => {
this.filteredBetreuer = result.data;
});
},
emptyBetreuer() {
this.$refs.projektbetreuerTable.tabulator.clearData();
},
resetForm() {
this.formData = this.getDefaultFormData();
if (this.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = '';
this.autocompleteSelectedBetreuer = null;
this.initialFormData = null;
if (this.projekttyp_kurzbz) this.setDefaultStunden(this.projekttyp_kurzbz);
this.disableVertragFields(false);
this.$refs.formProjektbetreuer.clearValidation();
},
resetModes() {
this.newMode = false;
this.editMode = false;
},
getDefaultFormData() {
let formData = {betreuerart_kurzbz : null, note: null};
for (const name in this.defaultFormDataValues) {
formData[name] = this.defaultFormDataValues[name];
}
return formData;
},
captureFormData() {
this.initialFormData = JSON.parse(JSON.stringify(this.formData)); // deep copy
},
// add own betreuer ids to betreuer liste
addIds(betreuerListe) {
for (const idx in betreuerListe) {
let betreuer = betreuerListe[idx];
betreuer.person_id_old = betreuer.person_id;
betreuer.betreuerart_kurzbz_old = betreuer.betreuerart_kurzbz;
betreuer.betreuer_id = parseInt(idx);
}
return betreuerListe;
},
// add the betreuer selected in automomplete to betreuer liste
getFormDataWithBetreuer() {
let preparedFormData = this.formData;
preparedFormData.projektarbeit_id = this.projektarbeit_id;
if (this.autocompleteSelectedBetreuer) {
preparedFormData.person_id = this.autocompleteSelectedBetreuer.person_id;
preparedFormData.name = this.autocompleteSelectedBetreuer.name;
preparedFormData.vorname = this.autocompleteSelectedBetreuer.vorname;
preparedFormData.nachname = this.autocompleteSelectedBetreuer.nachname;
}
return preparedFormData;
},
// get default values for stunden
getDefaultStunden(projekttyp_kurzbz) {
let stunden = '0.0';
if (projekttyp_kurzbz == 'Bachelor') stunden = this.config.defaultProjektbetreuerStunden;
if (projekttyp_kurzbz == 'Diplom') stunden = this.config.defaultProjektbetreuerStundenDiplom;
return stunden;
},
setDefaultStunden(projekttyp_kurzbz) {
this.projekttyp_kurzbz = projekttyp_kurzbz;
// if form data has not already been modified by user, set the default stunden
if (!this.formDataModified()) {
let defaultStunden = this.getDefaultStunden(projekttyp_kurzbz);
// adapt initial form data so it does not count as modified
if (this.initialFormData) this.initialFormData.stunden = defaultStunden;
// set default Stunden
this.formData.stunden = defaultStunden;
}
},
// check if form data has been modified since initial data has been captured
formDataModified() {
if (this.autocompleteSelectedBetreuer != null) return true;
for (const prop in this.initialFormData) {
if (typeof this.formData[prop] == 'undefined') return true;
if (this.formData[prop] != this.initialFormData[prop]) return true;
}
return false;
},
actionNewPerson() {
this.$refs.newPersonModal.reset();
this.$refs.newPersonModal.open();
},
actionKontaktdatenBearbeiten() {
if (!this.autocompleteSelectedBetreuer) return;
this.$refs.kontaktdatenModal.show();
},
// stuff to do after new person has been saved
personSaved(result) {
this.$api
.call(ApiStvProjektbetreuer.getPerson(result))
.then(response => {
// set the new person in Betreuer autocomplete field
this.autocompleteSelectedBetreuer = response.data;
})
.catch(this.$fhcAlert.handleSystemError)
},
// disable fields which are dependent on Vertrag status
disableVertragFields(statusAkzeptiert) {
this.vertragFieldsDisabled = statusAkzeptiert;
}
},
template: `
<div class="stv-details-projektbetreuer h-100 pb-3 row">
<div :class="this.config.showVertragsdetails ? 'col-9' : 'col-12'">
<legend>{{this.$p.t('projektarbeit','betreuerGross')}}</legend>
<core-filter-cmpt
ref="projektbetreuerTable"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
new-btn-show
:new-btn-label="this.$p.t('projektarbeit', 'betreuerGross')"
@click:new="actionNewProjektbetreuer"
>
</core-filter-cmpt>
<form-form ref="formProjektbetreuer" v-show="betreuerFormOpened" @submit.prevent>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-betreuer"
:label="$p.t('projektarbeit', 'betreuer')"
type="autocomplete"
optionLabel="name"
v-model="autocompleteSelectedBetreuer"
name="person_id"
:suggestions="filteredBetreuer"
@complete="searchBetreuer"
:min-length="3"
:disabled="vertragFieldsDisabled"
>
</form-input>
</div>
<div class="row mb-3">
<div class="col-6">
<button class="btn btn-outline-secondary" @click="actionNewPerson">{{ $p.t('projektarbeit', 'neuePersonAnlegen') }}</button>
</div>
<div class="col-6">
<button class="btn btn-outline-secondary float-end" @click="actionKontaktdatenBearbeiten">{{ $p.t('projektarbeit', 'kontaktdatenBearbeiten') }}</button>
</div>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektbetreuer-betreuerart"
:label="$p.t('projektarbeit', 'betreuerart')"
type="select"
v-model="formData.betreuerart_kurzbz"
name="betreuerart_kurzbz"
>
<option
v-for="art in arrBetreuerart"
:key="art.betreuerart_kurzbz"
:value="art.betreuerart_kurzbz"
>
{{art.beschreibung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektbetreuer-note"
:label="$p.t('projektarbeit', 'note')"
type="select"
v-model="formData.note"
name="note"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="note in arrNoten"
:key="note.note"
:value="note.note"
>
{{note.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<div class="col-6">
<form-input
container-class="stv-details-projektarbeit-stunden"
type="text"
name="stunden"
:label="$p.t('projektarbeit', 'stunden')"
:disabled="vertragFieldsDisabled"
v-model="formData.stunden"
>
</form-input>
</div>
<div class="col-6">
<form-input
container-class="stv-details-projektarbeit-stundensatz"
type="text"
name="stundensatz"
:label="$p.t('projektarbeit', 'stundensatz')"
:disabled="vertragFieldsDisabled"
v-model="formData.stundensatz"
>
</form-input>
</div>
</div>
</form-form>
<button class="btn btn-primary" v-show="betreuerFormOpened" @click="saveProjektbetreuer">
{{ $p.t('projektarbeit', 'betreuerSpeichern') }}
</button>
<!-- <div class = "mt-5" v-if="beurteilungDownloadLink !== null">
<div class="mb-1">
<a :href="beurteilungDownloadLink" class="btn btn-primary d-block" :class="{ 'disabled' : beurteilungDownloadLink === ''}">
{{ $p.t('projektarbeit', 'projektbeurteilungErstellen') }}
</a>
</div>
{{ autocompleteSelectedBetreuer?.person_id && beurteilungDownloadLink === '' ? $p.t('projektarbeit', 'projektarbeitNochNichtBeurteilt') : ''}}
</div> -->
</div>
<div class="col-3">
<vertrag ref="vertrag"
:vertrag_id="autocompleteSelectedBetreuer?.vertrag_id"
:person_id="autocompleteSelectedBetreuer?.person_id"
:betreuerProjektarbeit="initialFormData"
@vertragsstatusChanged="disableVertragFields">
</vertrag>
</div>
</div>
<!--Modal: new Person modal -->
<new-person ref="newPersonModal" :personOnly="true" @saved="personSaved"></new-person>
<!--Modal: KontaktdatenModal -->
<bs-modal
ref="kontaktdatenModal"
dialog-class="modal-xl modal-dialog-scrollable"
v-if="autocompleteSelectedBetreuer && autocompleteSelectedBetreuer.person_id">
<template #title>
<p class="fw-bold mt-3">{{$p.t('projektarbeit', 'kontaktdatenBearbeiten')}}</p>
</template>
<div class="row">
<div class="col-12">
<contact ref="contact" :uid="autocompleteSelectedBetreuer.person_id">
</contact>
</div>
</div>
</bs-modal>
`
}
@@ -0,0 +1,160 @@
import CoreForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import ApiVertrag from '../../../../../api/factory/stv/vertrag.js';
export default{
name: "ProjektarbeitVertrag",
components: {
CoreForm,
FormInput
},
emits: [
'canceledVertrag',
'vertragsstatusChanged'
],
props: {
vertrag_id: Number,
person_id: Number,
betreuerProjektarbeit: Object
},
inject: {
showVertragsdetails: {
from: 'configShowVertragsdetails',
default: false
}
},
data() {
return{
data: {
vertragsstatus: null,
vertragsstunden: null,
vertragsstunden_studiensemester_kurzbz: null
},
// status names for stages of Vertrag ("constants")
vertragsstatus_akzeptiert: 'Akzeptiert',
vertragsstatus_geaendert:'Geändert',
vertragsstatus_storniert: 'Storno'
}
},
watch: {
vertrag_id:
{
//deep: true,
handler(newVal, oldVal) {
this.resetForm();
if (newVal !== null && newVal !== undefined) this.getVertrag();
}
},
},
computed: {
vertragsstatus() {
// not show Vertragsstatus if no data
if (!this.data?.vertragsstatus || !this.betreuerProjektarbeit?.betreuerart_kurzbz) return;
const betragVertrag = Number(this.data.betrag) || 0;
const stundenVertrag = Number(this.data.vertragsstunden) || 0;
const semStunden = Number(this.betreuerProjektarbeit.stunden) || 0;
const stundensatz = Number(this.betreuerProjektarbeit.stundensatz) || 0;
const kostenAktuell = semStunden * stundensatz;
// Vertragsstunden amount should be same as Semesterstunden amount, otherwise there has been a change
let vertragsstatus = (stundenVertrag !== semStunden || betragVertrag !== kostenAktuell)
? this.vertragsstatus_geaendert
: (this.data.vertragsstatus || '');
// vertragsstatus changed to "akzeptiert"
this.$emit('vertragsstatusChanged', vertragsstatus == this.vertragsstatus_akzeptiert);
return vertragsstatus;
},
},
methods: {
getVertrag() {
if (this.showVertragsdetails === false)
return;
if (!this.vertrag_id)
return;
this.$api.call(ApiVertrag.getVertrag(this.vertrag_id))
.then(result => {
if (result.data.vertragsstatus != this.vertragsstatus_storniert) {
this.data = result.data;
}
})
.catch(this.$fhcAlert.handleSystemError);
},
cancelVertrag() {
this.$fhcAlert
.confirmDelete()
.then(result => result
? {vertrag_id: this.vertrag_id, person_id: this.person_id}
: Promise.reject({handled: true}))
.then(result => {
return this.$api.call(ApiVertrag.cancelVertrag({vertrag_id: this.vertrag_id, person_id: this.person_id}))
})
.then(result => {
this.resetForm();
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
this.$emit('canceledVertrag');
// vertragsstatus not "akzeptiert" anymore
this.$emit('vertragsstatusChanged', false);
})
.catch(this.$fhcAlert.handleSystemError);
},
resetForm() {
this.data = {
vertrag_id: null,
vertragsstatus: null,
vertragsstunden: null,
vertragsstunden_studiensemester_kurzbz: null
}
}
},
template: `
<core-form ref="form">
<fieldset class="overflow-hidden" v-if="showVertragsdetails">
<legend>
{{$p.t('lehre', 'vertragsdetails')}}
</legend>
<div class="mb-3">
{{ betreuerProjektarbeit?.betreuerart_kurzbz && betreuerProjektarbeit?.vertrag_id == null ? ' '+$p.t('lehre', 'nochKeinVertrag') : '' }}
</div>
<div class="row mb-3">
<div class="col-12">
{{ $p.t('lehre', 'vertragsstatus') }}: {{ vertragsstatus }}
</div>
</div>
<div class="row mb-3">
<div class="col-12">
{{$p.t('lehre', 'vertragurfassung')}}
</div>
</div>
<div class="row mb-3">
<div class="col-12">
{{ $p.t('lehre', 'semesterstunden') }}: {{ data.vertragsstunden }}
<br>
<span class="text-capitalize">{{ $p.t('lehre', 'studiensemester') }}</span>: {{ data.vertragsstunden_studiensemester_kurzbz }}
</div>
</div>
<div class="row mb-3" v-if="data?.vertragsstatus">
<div class="col-12">
<button
type="button"
class="btn btn-outline-secondary"
:disabled="vertragsstatus == vertragsstatus_storniert"
@click="cancelVertrag"
>
{{ $p.t('lehre', 'vertragStornieren') }}
</button>
</div>
</div>
</fieldset>
</core-form>
`
};
@@ -1,18 +1,28 @@
import {CoreFilterCmpt} from "../../filter/Filter.js";
import ListNew from './List/New.js';
import ListFilter from './List/Filter.js';
import draggable from '../../../directives/draggable.js';
export default {
name: "ListPrestudents",
components: {
CoreFilterCmpt,
ListNew
ListNew,
ListFilter
},
directives: {
draggable
},
inject: {
'lists': {
lists: {
from: 'lists',
required: true
},
$reloadList: {
from: '$reloadList',
required: true
},
currentSemester: {
from: 'currentSemester',
required: true
@@ -51,8 +61,12 @@ export default {
{title:"Vornamen", field:"vornamen", visible:false, headerFilter: true},
{title:"TitelPost", field:"titelpost", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Ersatzkennzeichen", field:"ersatzkennzeichen", headerFilter: true},
{title:"Geburtsdatum", field:"gebdatum", formatter:dateFormatter,
headerFilter: true, headerFilterFunc: function(headerValue, rowValue, rowData, filterParams) {
{
title: "Geburtsdatum",
field: "gebdatum",
formatter: dateFormatter,
headerFilter: true,
headerFilterFunc(headerValue, rowValue) {
const matches = headerValue.match(/^(([0-9]{2})\.)?([0-9]{2})\.([0-9]{4})?$/);
let comparestr = headerValue;
if(matches !== null) {
@@ -119,7 +133,7 @@ export default {
{
return Promise.resolve({ data: []});
}
return this.$api.call({url, params});
return this.$api.call({method: 'post', url, params});
},
ajaxResponse: (url, params, response) => {
return response?.data;
@@ -157,14 +171,63 @@ export default {
],
focusObj: null, // TODO(chris): this should be in the filter component
lastSelected: null,
filterKontoCount0: undefined,
filterKontoMissingCounter: undefined,
filter: [],
count: 0,
filteredcount: 0,
selectedcount: 0,
currentEndpointRawUrl: ''
}
},
computed: {
countsToHTML: function() {
return this.$p.t('global/ausgewaehlt')
+ ': <strong>' + (this.selectedcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gefiltert')
+ ': '
+ '<strong>' + (this.filteredcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gesamt')
+ ': <strong>' + (this.count || 0) + '</strong>';
},
selectedDragObject() {
return this.selected.map(item => {
let type, id;
if (item.uid) {
type = 'student';
id = item.uid;
} else if (item.prestudent_id) {
type = 'prestudent';
id = item.prestudent_id;
} else if (item.person_id) {
type = 'person';
id = item.person_id;
}
return {
...item,
type,
id
};
});
},
downloadConfig() {
return {
csv: {
formatter: 'csv',
file: this.fileString,
options: {
delimiter: ';',
bom: true,
}
}
};
},
fileString() {
let today = new Date().toLocaleDateString('en-GB')
.replace(/\//g, '_');
return "StudentList_" + today + ".csv";
}
},
methods: {
reload() {
this.$refs.table.reloadTable();
@@ -172,10 +235,19 @@ export default {
actionNewPrestudent() {
this.$refs.new.open();
},
rowSelectionChanged(data) {
rowSelectionChanged(data, rows) {
this.selectedcount = data.length;
this.lastSelected = this.selected;
this.$emit('update:selected', data);
// set selected elements draggable
const tableEl = this.$refs.table?.$refs?.table;
if (tableEl) {
const oldDragables = tableEl.querySelectorAll('[draggable]');
for (const draggable of oldDragables)
draggable.removeAttribute('draggable');
}
rows.forEach(row => row.getElement().draggable = true);
},
autoSelectRows(data) {
if (this.lastSelected) {
@@ -194,6 +266,10 @@ export default {
}
}
},
updateFilter(filter) {
this.filter = filter;
this.updateUrl();
},
updateUrl(endpoint, first) {
this.lastSelected = first ? undefined : this.selected;
@@ -214,14 +290,9 @@ export default {
encodeURIComponent(this.currentSemester)
);
const params = {}, filter = {};
if (this.filterKontoCount0)
filter.konto_count_0 = this.filterKontoCount0;
if (this.filterKontoMissingCounter)
filter.konto_missing_counter = this.filterKontoMissingCounter;
if (filter.konto_count_0 || filter.konto_missing_counter)
params.filter = filter;
const params = {};
if (this.filter.length)
params.filter = this.filter;
if (!this.$refs.table.tableBuilt) {
if (!this.$refs.table.tabulator) {
@@ -234,14 +305,22 @@ export default {
} else
this.$refs.table.tabulator.setData(endpoint.url, params);
},
dragCleanup(evt) {
if (evt.dataTransfer.dropEffect == 'none')
return; // aborted or wrong target
this.$reloadList();
},
onKeydown(e) { // TODO(chris): this should be in the filter component
if (!this.focusObj)
return;
var next;
switch (e.code) {
case 'Enter':
case 'Space':
e.preventDefault();
const e2 = new Event('click', e);
var e2 = new Event('click', e);
e2.altKey = e.altKey;
e2.ctrlKey = e.ctrlKey;
e2.shiftKey = e.shiftKey;
@@ -250,13 +329,13 @@ export default {
break;
case 'ArrowUp':
e.preventDefault();
var next = this.focusObj.previousElementSibling;
next = this.focusObj.previousElementSibling;
if (next)
this.changeFocus(this.focusObj, next);
break;
case 'ArrowDown':
e.preventDefault();
var next = this.focusObj.nextElementSibling;
next = this.focusObj.nextElementSibling;
if (next)
this.changeFocus(this.focusObj, next);
break;
@@ -296,24 +375,19 @@ export default {
}
}
},
computed: {
countsToHTML: function() {
return this.$p.t('global/ausgewaehlt')
+ ': <strong>' + (this.selectedcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gefiltert')
+ ': '
+ '<strong>' + (this.filteredcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gesamt')
+ ': <strong>' + (this.count || 0) + '</strong>';
}
},
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
// TODO(chris): filter component column chooser has no accessibilty features
template: `
<div class="stv-list h-100 pt-3">
<div class="tabulator-container d-flex flex-column h-100" :class="{'has-filter': filterKontoCount0 || filterKontoMissingCounter}" tabindex="0" @focusin="onFocus" @keydown="onKeydown">
<div
class="tabulator-container d-flex flex-column h-100"
:class="{'has-filter': filter.length}"
tabindex="0"
@focusin="onFocus"
@keydown="onKeydown"
v-draggable:copyLink.capture="selectedDragObject"
@dragend="dragCleanup"
>
<core-filter-cmpt
ref="table"
:description="countsToHTML"
@@ -322,6 +396,7 @@ export default {
table-only
:side-menu="false"
reload
:download="downloadConfig"
` + /* TODO(chris): Ausgeblendet für Testing
new-btn-show
*/`
@@ -331,29 +406,7 @@ export default {
<template #filter>
<div class="card">
<div class="card-body">
<div class="input-group mb-3">
<label class="input-group-text col-4" for="stv-list-filter-konto-count-0">{{ $p.t('stv/konto_filter_count_0') }}</label>
<select class="form-select" id="stv-list-filter-konto-count-0" v-model="filterKontoCount0" @input="$nextTick(updateUrl)">
<option v-for="typ in lists.buchungstypen" :key="typ.buchungstyp_kurzbz" :value="typ.buchungstyp_kurzbz">
{{ typ.beschreibung }}
</option>
</select>
<button v-if="filterKontoCount0" class="btn btn-outline-secondary" @click="filterKontoCount0 = undefined; updateUrl()">
<i class="fa fa-times"></i>
</button>
</div>
<div class="input-group">
<label class="input-group-text col-4" for="stv-list-filter-konto-missing-counter">{{ $p.t('stv/konto_filter_missing_counter') }}</label>
<select class="form-select" id="stv-list-filter-konto-missing-counter" v-model="filterKontoMissingCounter" @input="$nextTick(updateUrl)">
<option value="alle">{{ $p.t('stv/konto_all_types') }}</option>
<option v-for="typ in lists.buchungstypen" :key="typ.buchungstyp_kurzbz" :value="typ.buchungstyp_kurzbz">
{{ typ.beschreibung }}
</option>
</select>
<button v-if="filterKontoMissingCounter" class="btn btn-outline-secondary" @click="filterKontoMissingCounter = undefined; updateUrl()">
<i class="fa fa-times"></i>
</button>
</div>
<list-filter @change="updateFilter" />
</div>
</div>
</template>
@@ -0,0 +1,102 @@
import FilterItem from './Filter/Item.js';
import ApiStvApp from '../../../../api/factory/stv/app.js';
export default {
name: "ListPrestudentsFilter",
components: {
FilterItem
},
emits: [
'change'
],
data() {
return {
filters: [],
filterConfig: [// TODO(chris): get from BE!
{
name: 'stv/konto_filter_count_0',
type: 'konto',
fixed: {
missing: true,
usestdsem: true
},
dynamic: {
buchungstyp_kurzbz: {
type: 'select',
values: {
test1: 'Test1',
test2: 'Test2'
}
}
}
},
{
name: 'stv/konto_filter_missing_counter',
type: 'konto_counter',
dynamic: {
buchungstyp_kurzbz: {
type: 'select',
values: {
test1: 'Test1',
test2: 'Test2'
}
},
samestg: {
type: 'bool',
label: 'stv/konto',
default: true
}
}
}
]
}
},
computed: {
cleanFilters() {
return this.filters.filter(filter => {
if (!filter.type)
return false;
if (Object.values(filter).some(v => v === undefined))
return false;
return true;
});
}
},
watch: {
cleanFilters(n) {
this.$emit('change', n);
}
},
methods: {
add() {
this.filters.push({});
},
remove(index) {
this.filters.splice(index, 1);
}
},
created() {
this.$api
.call(ApiStvApp.configFilter())
.then(result => {
this.filterConfig = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
template: /* html */`
<div class="stv-list-filter h-100">
<button class="btn btn-outline-dark" type="button" @click="add">
<span class="fa-solid fa-plus" aria-hidden="true"></span>
{{ $p.t('filter/filter') }}
</button>
<filter-item
v-for="(filter, i) in filters"
:key="i"
v-model="filters[i]"
:filter-config="filterConfig"
class="mt-3"
@remove="remove(i)"
/>
</div>`
};
@@ -0,0 +1,113 @@
export default {
name: "FilterItem",
props: {
modelValue: Object,
filterConfig: Array
},
emits: [
'update:modelValue',
'remove'
],
data() {
return {
//type: this.modelValue.type
};
},
computed: {
value: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
}
},
filterid: {
get() {
return this.modelValue.filterid
},
set(filterid) {
const config = this.filterConfig.find(config => config.id == filterid);
const dynamic = Object.fromEntries(
Object.keys(config.dynamic || {}).map(key => {
return [
key,
config.dynamic[key].default
];
})
);
this.value = {
filterid,
type: config.type,
...(config.fixed || {}),
...dynamic
};
}
},
currentConfig() {
return this.filterConfig.find(config => config.id == this.filterid);
}
},
methods: {
update() {
this.$emit('update:modelValue', this.value);
}
},
template: /* html */`
<div class="stv-list-filter-item input-group">
<label
class="input-group-text col-4"
for="stv-list-filter-konto-count-0"
>
{{ $p.t('stv/filter_for') }}
</label>
<select
v-model="filterid"
id="stv-list-filter-konto-count-0"
class="form-select"
>
<option
v-for="(filter, i) in filterConfig"
:key="i"
:value="filter.id"
>
{{ filter.label }}
</option>
</select>
<template v-for="(conf, key) in currentConfig?.dynamic" :key="key">
<select
v-if="conf.type == 'select'"
v-model="modelValue[key]"
class="form-select"
@input="update"
>
<option
v-for="(label, value) in conf.values"
:key="conf.value_key ? label[conf.value_key] : value"
:value="conf.value_key ? label[conf.value_key] : value"
>
{{ conf.label_key ? label[conf.label_key] : label }}
</option>
</select>
<template v-else-if="conf.type == 'bool'">
<div class="input-group-text">
<label class="form-check-label">
<input
v-model="modelValue[key]"
type="checkbox"
class="form-check-input me-1"
@input="update"
>
{{ conf.label }}
</label>
</div>
</template>
</template>
<button
class="btn btn-outline-secondary"
:title="$p.t('ui/entfernen')"
:aria-label="$p.t('ui/entfernen')"
@click="$emit('remove')"
><i class="fa fa-times"></i></button>
</div>`
};
@@ -10,7 +10,7 @@ import ApiStvStudents from '../../../../api/factory/stv/students.js';
var _uuid = 0;
const FORMDATA_DEFAULT = {
address: {
func: 0,
func: 1,
nation: 'A'
},
geburtsnation: 'A',
@@ -33,7 +33,9 @@ export default {
inject: [
'lists'
],
emits: ['saved'],
props: {
personOnly: Boolean,
studiengangKz: Number,
studiensemesterKurzbz: String
},
@@ -108,7 +110,7 @@ export default {
return;
this.abortController.suggestions = new AbortController();
this.$api
.call(ApiStvStudents.check({
vorname: this.formData.vorname,
@@ -119,6 +121,11 @@ export default {
})
.then(result => this.suggestions = result.data)
.catch(error => {
if (error.code == 'ERR_BAD_REQUEST') {
return this.suggestions = [];
}
// NOTE(chris): repeat request
if (error.code != "ERR_CANCELED")
window.setTimeout(this.loadSuggestions, 100);
@@ -191,14 +198,22 @@ export default {
if (data.studiensemester_kurzbz === undefined)
data.studiensemester_kurzbz = this.studiensemesterKurzbz;
// TODO(chris): move to fhcapi.factory
this.$refs.form
.post('api/frontend/v1/stv/student/add', data)
.then(result => {
this.$fhcAlert.alertSuccess('Gespeichert');
this.$refs.modal.hide();
})
.catch(this.$fhcAlert.handleSystemError);
data.personOnly = this.personOnly;
this.$refs.form.call(
ApiStvStudents.add(data)
)
.then(result => {
this.$emit('saved', result.data);
this.$fhcAlert.alertSuccess('Gespeichert');
this.$refs.modal.hide();
})
.catch(this.$fhcAlert.handleSystemError);
},
setPerson(suggestion)
{
this.person = suggestion;
this.formData.address.func = -1;
}
},
created() {
@@ -215,7 +230,7 @@ export default {
<fhc-form ref="form" class="stv-list-new" @submit.prevent="send">
<bs-modal ref="modal" dialog-class="modal-lg modal-dialog-scrollable" @hidden-bs-modal="reset">
<template #title>
InteressentIn anlegen
{{ personOnly ? $p.t('person', 'personAnlegen') : $p.t('lehre', 'interessentAnlegen') }}
</template>
<template #default>
@@ -225,36 +240,38 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Nachname*"
:label="$p.t('person', 'nachname')+'*'"
type="text"
id="stv-list-new-nachname"
name="nachname"
v-model="formDataPerson['nachname']"
:disabled="person"
:disabled="!!person"
@input="loadSuggestions"
:min-length="3"
>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Vorname"
:label="$p.t('person', 'vorname')"
type="text"
:id="'stv-list-new-vorname-' + uuid"
name="vorname"
v-model="formDataPerson['vorname']"
:disabled="person"
:disabled="!!person"
@input="loadSuggestions"
:min-length="3"
>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Geburtsdatum"
:label="$p.t('person', 'geburtsdatum')"
type="datepicker"
uid="stv-list-new-gebdatum"
name="gebdatum"
v-model="formDataPerson['gebdatum']"
:disabled="person"
:disabled="!!person"
@update:model-value="loadSuggestions"
text-input
auto-apply
@@ -267,13 +284,13 @@ export default {
</div>
<!-- TODO(chris): more details -->
<table class="table caption-top table-striped table-hover">
<caption>Prüfung ob Person bereits existiert</caption>
<caption>{{ $p.t('person', 'personExistiertPruefung') }}</caption>
<tbody>
<tr
v-for="(suggestion, index) in suggestions"
:key="suggestion.person_id"
:class="{'active': index == 2}"
@click="(index == 2) ? suggestions.shift() : person=suggestion"
@click="(index == 2) ? suggestions.shift() : setPerson(suggestion)"
v-accessibility:tab.vertical
>
<td>{{suggestion.vorname + ' ' + suggestion.nachname}}</td>
@@ -286,34 +303,34 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Anrede"
:label="$p.t('person', 'anrede')"
type="text"
id="stv-list-new-anrede"
name="anrede"
v-model="formDataPerson['anrede']"
:disabled="person"
:disabled="!!person"
>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Titel (Pre)"
:label="$p.t('person', 'titelPre')"
type="text"
id="stv-list-new-titelpre"
name="titelpre"
v-model="formDataPerson['titelpre']"
:disabled="person"
:disabled="!!person"
>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Titel (Post)"
:label="$p.t('person', 'titelPost')"
type="text"
id="stv-list-new-titelpost"
name="titelpost"
v-model="formDataPerson['titelpost']"
:disabled="person"
:disabled="!!person"
>
</form-input>
</div>
@@ -321,36 +338,38 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Nachname*"
:label="$p.t('person', 'nachname')+'*'"
type="text"
id="stv-list-new-nachname"
name="nachname"
v-model="formDataPerson['nachname']"
:disabled="person"
:disabled="!!person"
@input="loadSuggestions"
:min-length="3"
>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Vorname"
:label="$p.t('person', 'vorname')"
type="text"
id="stv-list-new-vorname"
name="vorname"
v-model="formDataPerson['vorname']"
:disabled="person"
:disabled="!!person"
@input="loadSuggestions"
:min-length="3"
>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Weitere Vornamen"
:label="$p.t('person', 'weitereVornamen')"
type="text"
id="stv-list-new-vornamen"
name="vornamen"
v-model="formDataPerson['vornamen']"
:disabled="person"
:disabled="!!person"
>
</form-input>
</div>
@@ -358,12 +377,12 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Wahlname"
:label="$p.t('person', 'wahlname')"
type="text"
id="stv-list-new-wahlname"
name="wahlname"
v-model="formDataPerson['wahlname']"
:disabled="person"
:disabled="!!person"
>
</form-input>
</div>
@@ -371,24 +390,24 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Geschlecht*"
:label="$p.t('person', 'geschlecht')+'*'"
type="select"
id="stv-list-new-geschlecht"
name="geschlecht"
v-model="formDataPerson['geschlecht']"
:disabled="person"
:disabled="!!person"
>
<option v-for="geschlecht in lists.geschlechter" :key="geschlecht.geschlecht" :value="geschlecht.geschlecht">{{geschlecht.bezeichnung}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Geburtsdatum"
:label="$p.t('person', 'geburtsdatum')"
type="datepicker"
uid="stv-list-new-gebdatum"
name="gebdatum"
v-model="formDataPerson['gebdatum']"
:disabled="person"
:disabled="!!person"
@update:model-value="loadSuggestions"
text-input
auto-apply
@@ -400,7 +419,7 @@ export default {
</div>
</div>
<div v-if="person" class="row">
<div class="row">
<div class="col-sm-6 mb-3">
<form-input
type="select"
@@ -408,19 +427,19 @@ export default {
name="address[func]"
v-model="formData['address']['func']"
>
<option value="-1">Bestehende Adresse überschreiben</option>
<option value="1">Adresse hinzufügen</option>
<option value="0">Adresse nicht anlegen</option>
<option value="-1" v-if="person">{{ $p.t('person', 'bestehendeAdresseUeberschreiben') }}</option>
<option value="1">{{ $p.t('person', 'adresseHinzufuegen') }}</option>
<option value="0">{{ $p.t('person', 'adresseNichtAnlegen') }}</option>
</form-input>
</div>
</div>
<fieldset v-if="!person || formData['address']['func']">
<fieldset v-if="formData['address']['func'] != 0">
<legend>Adresse</legend>
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Land"
:label="$p.t('person', 'land')"
type="select"
id="stv-list-new-address-nation"
name="address[nation]"
@@ -434,7 +453,7 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="PLZ"
:label="$p.t('person', 'plz')"
type="text"
id="stv-list-new-address-plz"
name="address[plz]"
@@ -445,18 +464,18 @@ export default {
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Gemeinde"
:label="$p.t('person', 'gemeinde')"
type="select"
v-if="formData['address']['nation'] == 'A'"
id="stv-list-new-address-gemeinde"
name="address[gemeinde]"
v-model="formData['address']['gemeinde']"
>
<option v-if="!gemeinden.length" disabled>Bitte gültige PLZ wählen</option>
<option v-if="!gemeinden.length" disabled>$p.t('ui', 'bittePlzWaehlen')</option>
<option v-for="gemeinde in gemeinden" :key="gemeinde.name" :value="gemeinde.name">{{gemeinde.name}}</option>
</form-input>
<form-input
label="Gemeinde"
:label="$p.t('person', 'gemeinde')"
type="text"
v-else
id="stv-list-new-address-gemeinde"
@@ -467,7 +486,7 @@ export default {
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Ort"
:label="$p.t('person', 'ort')"
type="select"
v-if="formData['address']['nation'] == 'A'"
id="stv-list-new-address-ort"
@@ -478,7 +497,7 @@ export default {
<option v-for="ort in orte" :key="ort.ortschaftsname" :value="ort.ortschaftsname">{{ort.ortschaftsname}}</option>
</form-input>
<form-input
label="Ort"
:label="$p.t('person', 'ort')"
type="text"
v-else
id="stv-list-new-address-ort"
@@ -491,7 +510,7 @@ export default {
<div class="row">
<div class="col-12 mb-3">
<form-input
label="Adresse"
:label="$p.t('person', 'adresse')"
type="text"
id="stv-list-new-address-address"
name="address[address]"
@@ -505,7 +524,7 @@ export default {
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Geburtsnation"
:label="$p.t('person', 'geburtsnation')"
type="select"
id="stv-list-new-geburtsnation"
name="geburtsnation" class="form-select"
@@ -516,7 +535,7 @@ export default {
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Staatsbürgerschaft"
:label="$p.t('person', 'staatsbuergerschaft')"
type="select"
id="stv-list-new-staatsbuergerschaft"
name="staatsbuergerschaft"
@@ -539,7 +558,7 @@ export default {
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Telefon"
:label="$p.t('person', 'telefon')"
type="text"
id="stv-list-new-telefon"
name="telefon"
@@ -549,7 +568,7 @@ export default {
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Mobil"
:label="$p.t('person', 'mobil')"
type="text"
id="stv-list-new-mobil"
name="mobil"
@@ -558,126 +577,128 @@ export default {
</form-input>
</div>
</div>
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Letzte Ausbildung"
type="select"
id="stv-list-new-letzteausbildung"
name="letzteausbildung"
v-model="formData['letzteausbildung']"
>
<option v-for="ausbildung in lists.ausbildungen" :key="ausbildung.ausbildungcode" :value="ausbildung.ausbildungcode">{{ausbildung.ausbildungbez}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Ausbildungsart"
type="text"
id="stv-list-new-ausbildungsart"
name="ausbildungsart"
v-model="formDataPerson['ausbildungsart']"
>
</form-input>
</div>
</div>
<div class="row">
<div class="col-sm-8 mb-3">
<form-input
label="Anmerkungen"
type="textarea"
id="stv-list-new-anmerkungen"
name="anmerkungen"
v-model="formDataPerson['anmerkungen']"
>
</form-input>
</div>
</div>
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Studiengang*"
type="select"
id="stv-list-new-studiengang_kz"
name="studiengang_kz"
v-model="formDataStg"
>
<option v-for="stg in lists.active_stgs" :key="stg.studiengang_kz" :value="stg.studiengang_kz">{{stg.kuerzel}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Studiensemester*"
type="select"
id="stv-list-new-studiensemester_kurzbz"
name="studiensemester_kurzbz"
v-model="formDataSem"
>
<option v-for="sem in semester" :key="sem.studiensemester_kurzbz" :value="sem.studiensemester_kurzbz">{{sem.studiensemester_kurzbz}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Ausbildungssemester*"
type="select"
id="stv-list-new-ausbildungssemester"
name="ausbildungssemester"
v-model="formData['ausbildungssemester']"
:disabled="formData['incoming']"
@input="loadStudienplaene"
>
<option v-for="sem in Array.from({length:8}).map((u,i) => i+1)" :key="sem" :value="sem">{{sem}}. Semester</option>
</form-input>
</div>
</div>
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="OrgForm"
type="select"
id="stv-list-new-orgform_kurzbz"
name="orgform_kurzbz"
v-model="formData['orgform_kurzbz']"
@input="loadStudienplaene"
>
<option value="">-- keine Auswahl --</option>
<option v-for="orgform in lists.orgforms" :key="orgform.orgform_kurzbz" :value="orgform.orgform_kurzbz">{{orgform.bezeichnung}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
label="Studienplan"
type="select"
id="stv-list-new-studienplan_id"
name="studienplan_id"
v-model="formData['studienplan_id']"
>
<option value="">-- keine Auswahl --</option>
<option v-for="plan in studienplaene" :key="plan.studienplan_id" :value="plan.studienplan_id">{{plan.bezeichnung}}</option>
</form-input>
</div>
</div>
<div class="row">
<div class="col-10 mb-3">
<div class="form-check">
<fieldset v-if="!personOnly">
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
label="Incoming"
type="checkbox"
id="stv-list-new-incoming"
name="incoming"
v-model="formData['incoming']"
value="1"
:label="$p.t('lehre', 'letzeAusbildung')"
type="select"
id="stv-list-new-letzteausbildung"
name="letzteausbildung"
v-model="formData['letzteausbildung']"
>
<option v-for="ausbildung in lists.ausbildungen" :key="ausbildung.ausbildungcode" :value="ausbildung.ausbildungcode">{{ausbildung.ausbildungbez}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
:label="$p.t('lehre', 'ausbildungsart')"
type="text"
id="stv-list-new-ausbildungsart"
name="ausbildungsart"
v-model="formDataPerson['ausbildungsart']"
>
</form-input>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-8 mb-3">
<form-input
:label="$p.t('lehre', 'anmerkungen')"
type="textarea"
id="stv-list-new-anmerkungen"
name="anmerkungen"
v-model="formDataPerson['anmerkungen']"
>
</form-input>
</div>
</div>
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
:label="$p.t('lehre', 'studiengang')+'*'"
type="select"
id="stv-list-new-studiengang_kz"
name="studiengang_kz"
v-model="formDataStg"
>
<option v-for="stg in lists.active_stgs" :key="stg.studiengang_kz" :value="stg.studiengang_kz">{{stg.kuerzel}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
:label="$p.t('lehre', 'studiensemester')+'*'"
type="select"
id="stv-list-new-studiensemester_kurzbz"
name="studiensemester_kurzbz"
v-model="formDataSem"
>
<option v-for="sem in semester" :key="sem.studiensemester_kurzbz" :value="sem.studiensemester_kurzbz">{{sem.studiensemester_kurzbz}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
:label="$p.t('lehre', 'ausbildungssemester')+'*'"
type="select"
id="stv-list-new-ausbildungssemester"
name="ausbildungssemester"
v-model="formData['ausbildungssemester']"
:disabled="formData['incoming']"
@input="loadStudienplaene"
>
<option v-for="sem in Array.from({length:8}).map((u,i) => i+1)" :key="sem" :value="sem">{{sem}}. Semester</option>
</form-input>
</div>
</div>
<div class="row">
<div class="col-sm-4 mb-3">
<form-input
:label="$p.t('lehre', 'organisationsform')"
type="select"
id="stv-list-new-orgform_kurzbz"
name="orgform_kurzbz"
v-model="formData['orgform_kurzbz']"
@input="loadStudienplaene"
>
<option value="">-- keine Auswahl --</option>
<option v-for="orgform in lists.orgforms" :key="orgform.orgform_kurzbz" :value="orgform.orgform_kurzbz">{{orgform.bezeichnung}}</option>
</form-input>
</div>
<div class="col-sm-4 mb-3">
<form-input
:label="$p.t('lehre', 'studienplan')"
type="select"
id="stv-list-new-studienplan_id"
name="studienplan_id"
v-model="formData['studienplan_id']"
>
<option value="">-- keine Auswahl --</option>
<option v-for="plan in studienplaene" :key="plan.studienplan_id" :value="plan.studienplan_id">{{plan.bezeichnung}}</option>
</form-input>
</div>
</div>
<div class="row">
<div class="col-10 mb-3">
<div class="form-check">
<form-input
label="Incoming"
type="checkbox"
id="stv-list-new-incoming"
name="incoming"
v-model="formData['incoming']"
value="1"
>
</form-input>
</div>
</div>
</div>
</fieldset>
</template>
</template>
<template #footer>
<button v-if="person !== null" type="button" class="btn btn-secondary" @click="person = null"><i class="fa fa-chevron-left"></i>Zurück</button>
<button type="submit" class="btn btn-primary">{{ person === null ? 'Person anlegen' : 'InteressentIn anlegen' }}</button>
<button v-if="person !== null" type="button" class="btn btn-secondary" @click="person = null; formData.address.func = 1;"><i class="fa fa-chevron-left"></i>{{ $p.t('ui', 'zurueck') }}</button>
<button type="submit" class="btn btn-primary">{{ person === null || personOnly ? $p.t('person', 'personAnlegen') : $p.t('lehre', 'interessentAnlegen') }}</button>
</template>
</bs-modal>
</fhc-form>`
};
};
@@ -1,10 +1,28 @@
import ApiStvVerband from '../../../api/factory/stv/verband.js';
import drop from '../../../directives/drop.js';
import dragClick from '../../../directives/dragClick.js';
import ApiStvGroups from '../../../api/factory/stv/group.js';
import ApiStvDetails from '../../../api/factory/stv/details.js';
export default {
components: {
PvTreetable: primevue.treetable,
PvColumn: primevue.column
},
directives: {
drop,
dragClick
},
inject: {
$reloadList: {
from: '$reloadList',
required: true
},
currentSemester: {
from: 'currentSemester',
required: true
}
},
emits: [
'selectVerband'
],
@@ -201,6 +219,51 @@ export default {
this.selectedKey = {[currentNode.key]: true};
this.onSelectTreeNode(currentNode);
},
async toggleTreeNode(node) {
if (this.expandedKeys[node.key]) {
delete this.expandedKeys[node.key];
} else if (!node.leaf) {
await this.onExpandTreeNode(node);
this.expandedKeys[node.key] = true;
}
},
getStudentAjaxId(student) {
let res = student.id;
if (student.vorname && student.nachname)
res += ' (' + student.vorname + ' ' + student.nachname + ')';
return res;
},
dropStudents(node, students) {
const data = node.data;
let endpoint;
if (data.gruppe_kurzbz) {
endpoint = students.map(student => [
this.getStudentAjaxId(student),
ApiStvGroups.add(
student.id,
data.gruppe_kurzbz,
this.currentSemester
)
]);
} else {
const { semester, verband, gruppe } = data;
const params = { semester, verband, gruppe };
endpoint = students.map(student => [
this.getStudentAjaxId(student),
ApiStvDetails.saveStudent(
student.id,
this.currentSemester,
params
)
]);
}
return this.$api
.call(endpoint)
.then(this.$reloadList)
.catch(this.$fhcAlert.handleSystemError);
}
},
mounted() {
@@ -262,10 +325,21 @@ export default {
</template>
<template #body="{ node }">
<span
v-if="['semester', 'verband', 'gruppe', 'gruppe_kurzbz'].some(key => node.data.hasOwnProperty(key))"
:data-tree-item-key="node.key"
:title="node.data.studiengang_kz"
v-drag-click="() => toggleTreeNode(node)"
v-drop:link-strict.student-collection="(evt, students) => dropStudents(node, students)"
>
{{node.data.name}}
{{ node.data.name }}
</span>
<span
v-else
:data-tree-item-key="node.key"
:title="node.data.studiengang_kz"
v-drag-click="() => toggleTreeNode(node)"
>
{{ node.data.name }}
</span>
</template>
</pv-column>
+3 -1
View File
@@ -158,10 +158,12 @@ export default {
template: `
<template v-if="useprimevue">
<tabview
<tabview
class="d-flex flex-column"
:scrollable="true"
:lazy="true"
:activeIndex="calcActiveIndex"
:pt="{navContainer:{style: 'flex: 0 0 auto;'}, panelContainer:{class: 'overflow-y-scroll', style: 'flex: 1 1 auto;'}}"
@tab-click="handleTabClick"
>
<tabpanel
+53
View File
@@ -0,0 +1,53 @@
import { bindDragEnterLeave } from '../helpers/DragAndDrop.js';
export default {
mounted(el, binding) {
const delay = parseInt(binding.arg) || 300;
let timeout = null;
function startCountdown() {
timeout = window.setTimeout(binding.value, delay);
}
function stopCountdown() {
if (timeout)
window.clearTimeout(timeout);
timeout = null;
}
let lastTarget;
let lastX;
let lastY;
function onDragOver(evt) {
if (lastX != evt.offsetX || lastY != evt.offsetY || lastTarget != evt.target) {
// moved
lastTarget = evt.target;
lastX = evt.offsetX;
lastY = evt.offsetY;
stopCountdown();
startCountdown();
}
}
function onEnter(evt) {
lastTarget = evt.target;
lastX = evt.offsetX;
lastY = evt.offsetY;
el.addEventListener('dragover', onDragOver);
startCountdown();
}
function onLeave() {
stopCountdown();
el.removeEventListener('dragover', onDragOver);
}
el.fhcDragClickCleanup = bindDragEnterLeave(el, onEnter, onLeave);
},
beforeUnmount(el) {
el.fhcDragClickCleanup();
delete el.fhcDragClickCleanup;
}
}
+101
View File
@@ -0,0 +1,101 @@
import { setTransferData, convertToValidDragObject } from '../helpers/DragAndDrop.js';
const EFFECTS = [
'none',
'copy',
'copyLink',
'copyMove',
'link',
'linkMove',
'move',
'all',
'uninitialized'
];
export default {
mounted(el, binding) {
updateValue(el, binding.value);
updateEffectAllowed(el, binding.arg);
// if modifier capture is set we assume it's on a parent element
// i.e: for dragging multiple elements
// otherwise set draggable attribute
if (!binding.modifiers.capture) {
el.draggable = true;
}
const bcc = new BroadcastChannel('fhc-dnd');
let blocked = false;
function onStart(evt) {
const value = el.dataset.fhcDraggableValue;
if (value) {
setTransferData(evt, JSON.parse(value), true);
if (el.dataset.fhcEffectAllowed)
evt.dataTransfer.effectAllowed = el.dataset.fhcEffectAllowed;
bcc.onmessage = e => {
if (e.data == 'block') {
blocked = true;
} else if (e.data == 'release') {
let evt = null;
if (blocked && blocked.evt) {
evt = blocked.evt;
}
blocked = false;
if (evt)
el.dispatchEvent(evt);
}
};
} else {
evt.preventDefault();
}
}
function onEnd(evt) {
if (blocked) {
blocked = {
evt,
dt: evt.dataTransfer
};
evt.stopPropagation();
el.dispatchEvent(new DragEvent("beforedragend", evt));
} else {
bcc.onmessage = () => {};
}
}
el.addEventListener('dragstart', onStart, binding.modifiers.capture);
el.addEventListener('dragend', onEnd, true);
el.fhcDraggableCleanup = () => {
el.removeEventListener('dragstart', onStart, binding.modifiers.capture);
el.removeEventListener('dragend', onEnd, true);
};
},
updated(el, binding) {
updateValue(el, binding.value);
updateEffectAllowed(el, binding.arg);
},
beforeUnmount(el) {
el.fhcDraggableCleanup();
delete el.fhcDraggableCleanup;
}
}
// Helper functions
function updateValue(el, value) {
value = convertToValidDragObject(value);
if (value) {
el.dataset.fhcDraggableValue = JSON.stringify(value);
} else if (el.dataset.fhcDraggableValue) {
delete el.dataset.fhcDraggableValue;
}
}
function updateEffectAllowed(el, effectAllowed) {
if (effectAllowed && EFFECTS.includes(effectAllowed)) {
el.dataset.fhcEffectAllowed = effectAllowed;
} else if (el.dataset.fhcEffectAllowed) {
delete el.dataset.fhcEffectAllowed;
}
}
+77
View File
@@ -0,0 +1,77 @@
import { getValidTransferData, eventHasTypes, bindDragEnterLeave } from '../helpers/DragAndDrop.js';
const EFFECTS = [
'move',
'copy',
'link',
'none'
];
export default {
mounted(el, binding) {
const allowedTypes = Object.keys(binding.modifiers);
allowedTypes.forEach(type => {
if (type.substr(-11) == '-collection') {
const singleType = type.substr(0, type.length-11);
if (!allowedTypes.includes(singleType))
allowedTypes.push(singleType);
}
});
const strict = binding.arg.match(/(strict-|-strict)/);
const arg = binding.arg.replace(/(strict-|-strict)/, '');
const effect = EFFECTS.includes(arg) ? arg : null;
const bcc = new BroadcastChannel('fhc-dnd');
let allowed = false;
function onEnter(evt) {
allowed = eventHasTypes(evt, allowedTypes, strict);
if (allowed) {
evt.preventDefault();
bcc.postMessage('block');
}
}
function onLeave(evt, wasDropped) {
if (allowed && !wasDropped) {
bcc.postMessage('release');
}
}
function onOver(evt) {
if (allowed) {
evt.preventDefault();
if (effect)
evt.dataTransfer.dropEffect = effect;
}
}
function onDrop(evt) {
let result = getValidTransferData(evt, allowedTypes, strict);
if (!Array.isArray(result) && !binding.modifiers[result.type] && allowedTypes.includes(result.type + '-collection'))
result = [result];
const res = binding.value(evt, result);
if (res instanceof Promise) {
res.then(r => {
bcc.postMessage('release');
return r;
});
} else {
bcc.postMessage('release');
}
}
const cleanupEnterLeave = bindDragEnterLeave(el, onEnter, onLeave);
el.addEventListener('dragover', onOver);
el.addEventListener('drop', onDrop);
el.fhcDropCleanup = () => {
cleanupEnterLeave();
el.removeEventListener('dragover', onOver);
el.removeEventListener('drop', onDrop);
};
},
beforeUnmount(el) {
el.fhcDropCleanup();
delete el.fhcDropCleanup;
}
}
+357 -56
View File
@@ -1,67 +1,368 @@
/**
* TODO(chris): This is only a prototype!!!
*/
const DragAndDrop = {
TYPE_LE: "lehreinheit",
TYPE_VEVENT: "vevent",
getValidTransferData(event, allowedTypes) {
const json = event.dataTransfer.getData('text');
let obj;
try {
obj = JSON.parse(json);
if (!obj.type)
return null;
if (allowedTypes && !allowedTypes.includes(obj.type))
return null;
} catch (error) {
return null;
}
return obj;
const TYPE_DEFINITION = {
lehreinheit: {
id: "lehreinheit_id",
dragIcon: "fa-solid fa-chalkboard-user",
extras: [
"stundenblockung"
]
},
isValidTransferData(event, allowedTypes) {
return this.getValidTransferData(event, allowedTypes) ? true : false;
vevent: {
id: "uid",
dragIcon: "fa-solid fa-calendar",
extras: [
"dtstart",
"dtend",
"summary"
]
},
getTransferData(event) {
const json = event.dataTransfer.getData('text');
return JSON.parse(json);
person: {
id: "person_id",
dragIcon: "fa-solid fa-user"
},
setTransferData(event, data) {
switch (data.type) {
case DragAndDrop.TYPE_LE:
data = DragAndDrop.fromLe(data);
break;
default:
if (data.dtstart && data.dtend && data.uid && data.summary) {
data = DragAndDrop.fromVEvent(data);
break;
}
return false; // No type found => abort
}
event.dataTransfer.setData('text', JSON.stringify(data));
return true;
student: {
id: "student_uid",
dragIcon: "fa-solid fa-user-graduate"
},
fromLe(data) {
const {
type = DragAndDrop.TYPE_LE,
lehreinheit_id: id,
stundenblockung
} = data;
return { type, id, stundenblockung };
},
fromVEvent(data) {
const {
type = DragAndDrop.TYPE_VEVENT,
uid: id,
dtstart,
dtend,
summary
} = data;
return { type, id, dtstart, dtend, summary };
prestudent: {
id: "prestudent_id",
dragIcon: "fa-solid fa-user-graduate text-muted"
}
// TODO: IMPLEMENT OTHER TYPES
};
export default DragAndDrop;
const VALID_TYPES = Object.keys(TYPE_DEFINITION);
const TYPE_CONSTANTS = Object.keys(TYPE_DEFINITION).reduce((res, type) => {
res['TYPE_' + type.toUpperCase()] = type;
return res;
}, {});
function isValidDragObject(value) {
if (!value)
return false;
if (Array.isArray(value))
return value.every(isValidDragObject);
if (!value.type)
return false;
if (value.type.substr(-11) == '-collection') {
if (!Object.prototype.hasOwnProperty.call(value, 'values'))
return false;
if (!VALID_TYPES.includes(value.type.substr(0, value.type.length-11)))
return false;
} else {
if (!Object.prototype.hasOwnProperty.call(value, 'id'))
return false;
if (!VALID_TYPES.includes(value.type))
return false;
if (TYPE_DEFINITION[value.type].extras) {
if (!TYPE_DEFINITION[value.type].extras.every(extra => Object.prototype.hasOwnProperty.call(value, extra)))
return false;
}
}
return true;
}
function getValidTransferData(event, allowedTypes, strict) {
let obj = null;
try {
obj = getTransferData(event, strict);
if (!obj)
return null;
if (!strict && Array.isArray(obj)) {
obj = obj.filter(isValidDragObject);
if (!obj.length)
return null;
} else if (!isValidDragObject(obj))
return null;
if (allowedTypes && allowedTypes.length) {
if (Array.isArray(obj)) {
if (strict && !obj.every(v => allowedTypes.includes(v.type))) {
return null;
} else if (!strict) {
obj = obj.filter(v => allowedTypes.includes(v.type));
if (!obj.length)
return null;
}
} else if (!allowedTypes.includes(obj.type)) {
return null;
}
}
} catch(_error) {
return null;
}
if (Array.isArray(obj) && obj.length == 1)
return obj.find(Boolean);
return obj;
}
function isValidTransferData(event, allowedTypes, strict) {
return getValidTransferData(event, allowedTypes, strict) ? true : false;
}
function getTransferData(event, strict) {
const result = [];
for (const type of event.dataTransfer.types) {
if (type.substr(0, 16) != 'application/fhc-') {
if (strict)
return null;
continue;
}
let base_type = type.substr(16);
let collection = false;
if (base_type.substr(-11) == '-collection') {
base_type = base_type.substr(0, base_type.length-11);
collection = true;
}
if (!VALID_TYPES.includes(base_type)) {
if (strict)
return null;
continue;
}
let data = JSON.parse(event.dataTransfer.getData(type));
if (collection)
result.push(...data.values);
else
result.push(data);
}
if (!result.length)
return null;
if (result.length == 1)
return result[0];
return result;
}
function convertToValidDragObject(data, strict) {
if (Array.isArray(data)) {
const converted = data.map(convertToValidDragObject).filter(Boolean);
if (!converted.length)
return undefined;
if (strict && converted.length != data.length)
return undefined;
const sorted = converted.reduce((res, item) => {
if (!res[item.type])
res[item.type] = [];
res[item.type].push(item);
return res;
}, {});
return Object.entries(sorted).map(([type, values]) => {
if (values.length > 1) {
return {
type: type + '-collection',
values
};
}
return values[0];
});
}
if (Object.prototype.hasOwnProperty.call(data, 'type') && isValidDragObject(data)) {
return data;
}
const found = Object.entries(TYPE_DEFINITION).find(([ , typedef ]) => {
if (!Object.prototype.hasOwnProperty.call(data, typedef.id))
return false;
if (typedef.extras) {
if (!typedef.extras.every(extra => Object.prototype.hasOwnProperty.call(data, extra)))
return false;
}
return true;
});
if (!found) {
return undefined;
}
const [ type, typedef ] = found;
const newData = {};
newData.type = type;
newData.id = data[typedef.id];
if (typedef.extras)
typedef.extras.forEach(extra => newData[extra] = data[extra]);
return newData;
}
function setTransferData(event, validDragObject, setDragImage = false) {
if (setDragImage) {
const dragItems = Array.isArray(validDragObject) ? validDragObject : [ validDragObject ];
const dragElements = dragItems.map(item => {
const icon = document.createElement('i');
const label = document.createElement('span');
const iconContainer = document.createElement('span');
iconContainer.className = 'btn btn-outline-dark bg-light';
label.className = 'small';
if (TYPE_DEFINITION[item.type]) {
icon.className = TYPE_DEFINITION[item.type].dragIcon || 'fa-solid fa-question';
label.textContent = item.id;
} else if (item.type.substr(-11) == '-collection' && TYPE_DEFINITION[item.type.substr(0, item.type.length-11)]) {
iconContainer.style.boxShadow = '3px 3px var(--bs-btn-border-color)';
icon.className = TYPE_DEFINITION[item.type.substr(0, item.type.length-11)].dragIcon || 'fa-solid fa-question';
label.textContent = 'x' + item.values.length;
} else {
icon.className = 'fa-solid fa-question';
label.textContent = item.id || '';
}
iconContainer.append(icon);
const itemContainer = document.createElement('div');
itemContainer.className = 'd-flex flex-column align-items-center gap-2 small';
itemContainer.append(iconContainer, label);
return itemContainer;
});
const container = document.createElement('div');
container.className = 'd-flex flex-row gap-2 small';
container.append(...dragElements);
document.body.append(container);
event.dataTransfer.setDragImage(container, -25, 0);
requestAnimationFrame(() => {
document.body.removeChild(container);
});
}
if (Array.isArray(validDragObject)) {
return validDragObject.forEach(data => setTransferData(event, data));
}
event.dataTransfer.setData('application/fhc-' + validDragObject.type, JSON.stringify(validDragObject));
}
/**
* check if the dataTransfer types are in the allowed types array
* if strict is disabled at least one type must be the allowed array
* otherwise all types have to be in the allowed array
*
* @param Event event
* @param Array allowedTypes
* @param Boolean strict
*/
function eventHasTypes(event, allowedTypes, strict) {
if (!allowedTypes || !allowedTypes.length)
allowedTypes = VALID_TYPES;
allowedTypes = allowedTypes.map(type => 'application/fhc-' + type);
const dataTypes = [...event.dataTransfer.types];
// NOTE(chris): if dragging across browsers the dataTransfer object is
// set to a default one without data. Since we do not support dragging
// across browsers (yet) we return false which will disallow dropping.
if (!dataTypes.length)
return false;
if (!strict)
return allowedTypes.some(type => [...event.dataTransfer.types].includes(type));
return [...event.dataTransfer.types].every(type => allowedTypes.includes(type));
}
function bindDragEnterLeave(el, onEnter, onLeave) {
// NOTE(chris): add save dragenter and dragleave events
// that won't fire when hovering over child elements
let skipLeave = false;
let skipLeaveParent = true;
function init(evt) {
skipLeave = false;
skipLeaveParent = true;
// add global listeners
window.addEventListener('dragenter', globalDragenter, true);
window.addEventListener('dragleave', globalDragleave, true);
window.addEventListener('drop', globalDrop, true);
// call enter
onEnter(evt);
// remove self
el.removeEventListener('dragenter', init);
}
function cleanup(evt, wasDropped) {
// remove global listeners
window.removeEventListener('dragenter', globalDragenter, true);
window.removeEventListener('dragleave', globalDragleave, true);
window.removeEventListener('drop', globalDrop, true);
// call leave
onLeave(evt, wasDropped);
// add init
el.addEventListener('dragenter', init);
}
function globalDragenter(evt) {
skipLeaveParent = false;
if (el != evt.target && !el.contains(evt.target)) {
cleanup(evt);
} else {
skipLeave = true;
}
}
function globalDragleave(evt) {
if (el != evt.target && !el.contains(evt.target)) {
if (skipLeaveParent) {
skipLeaveParent = false;
return;
}
} else {
if (skipLeave) {
skipLeave = false;
return;
}
}
cleanup(evt);
}
function globalDrop(evt) {
cleanup(evt, true);
}
el.addEventListener('dragenter', init);
return () => {
// cleanup
el.removeEventListener('dragenter', init);
}
}
export {
isValidDragObject,
getValidTransferData,
isValidTransferData,
getTransferData,
convertToValidDragObject,
setTransferData,
eventHasTypes,
bindDragEnterLeave
};
export default {
...TYPE_CONSTANTS,
isValidDragObject,
getValidTransferData,
isValidTransferData,
getTransferData,
convertToValidDragObject,
setTransferData,
eventHasTypes,
bindDragEnterLeave
};
+4
View File
@@ -0,0 +1,4 @@
export function capitalize(string) {
if (!string) return '';
return string[0].toUpperCase() + string.slice(1);
}
+3
View File
@@ -548,6 +548,9 @@ export default {
} else {
console.error("FhcApi: method not allowed:", method);
}
},
getErrorHandler(config) {
return get_error_handler(config);
}
};
+10
View File
@@ -332,6 +332,12 @@ foreach($prestudent_arr as $prest_id)
{
$studiengangbezeichnung = $studienordnung->__get('studiengangbezeichnung');
$studiengangbezeichnung_englisch = $studienordnung->__get('studiengangbezeichnung_englisch');
$akadgrad = new akadgrad();
if ($akadgrad->load($studienordnung->__get('akadgrad_id')))
{
$akadgrad_titel_studienordnung = $akadgrad->titel;
$akadgrad_kurzbz_studienordnung = $akadgrad->akadgrad_kurzbz;
}
}
}
$studiengang_bezeichnung = empty($studiengangbezeichnung) ? $studiengang->bezeichnung : $studiengangbezeichnung;
@@ -428,6 +434,8 @@ foreach($prestudent_arr as $prest_id)
//Wenn Quereinsteiger stimmt studiengang_maxsemester nicht mit der tatsaechlichen Ausbildungsdauer ueberein
$student_maxsemester = ($studiengang->max_semester-$ausbildungssemester)+1;
// TODO: where to get semester duration for master Lehrgaenge?
echo "\t\t<student_maxsemester>".$student_maxsemester."</student_maxsemester>\n";
echo "\t\t<student_anzahljahre>".($student_maxsemester/2)."</student_anzahljahre>\n";
@@ -452,6 +460,8 @@ foreach($prestudent_arr as $prest_id)
echo "\t\t<akadgrad>".$akadgrad_titel."</akadgrad>\n";
echo "\t\t<akadgrad_kurzbz>".$akadgrad_kurzbz."</akadgrad_kurzbz>\n";
echo "\t\t<akadgrad_studienordnung>".($akadgrad_titel_studienordnung ?? '')."</akadgrad_studienordnung>\n";
echo "\t\t<akadgrad_kurzbz_studienordnung>".($akadgrad_kurzbz_studienordnung ?? '')."</akadgrad_kurzbz_studienordnung>\n";
echo "\t\t<datum_aktuell>".$datum_aktuell."</datum_aktuell>\n";
+6 -5
View File
@@ -1160,12 +1160,13 @@ function checkNote($note_alt, $note_neu)
'4' => 'ea',
'5' => 'tg',
'6' => 'met',
'7' => 'ar',
'8' => 'nb',
'9' => '5',
'10' => 'nea');
'7' => 'b',
'8' => 'ar',
'9' => 'nb',
'10' => '5',
'11' => 'nea');
for($i = 0; $i<=9; $i++)
for($i = 0; $i<=11; $i++)
{
if($note_alt == $arrayNotenPriority[$i])
$priority_alt = $i;
+2 -2
View File
@@ -335,14 +335,14 @@ if (isset($_REQUEST["xmlformat"]) && $_REQUEST["xmlformat"] == "xml")
$anrechnung = new anrechnung();
$anrechnung->getAnrechnungPrestudent($student->prestudent_id, null, $lehrveranstaltung_id);
$xml .= "<studienverpflichtung>";
$xml .= "<studienverpflichtung><![CDATA[";
$lehrveranstaltung_id_kompatibel = "";
if(count($anrechnung->result) === 1)
{
$lehrveranstaltung_id_kompatibel = $anrechnung->result[0]->lehrveranstaltung_id;
$xml .= $anrechnung->result[0]->lehrveranstaltung_bez;
}
$xml .= "</studienverpflichtung>";
$xml .= "]]></studienverpflichtung>";
if($lehrveranstaltung_id_kompatibel != "")
{

Some files were not shown because too many files have changed in this diff Show More