From bf1bc8d1a97dbb52c2d1bd30a295a3909058da22 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Sun, 13 Oct 2024 13:32:15 +0200 Subject: [PATCH 001/312] added new contact type email unverifiziert and adresse type meldeadresse (mainly for electronic onboarding) --- system/dbupdate_3.4.php | 1 + ...14_electronic_onboarding_anbindung_ida.php | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 11880fd55..d3b7b454d 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -58,6 +58,7 @@ require_once('dbupdate_3.4/17513_Entwicklungsteam.php'); require_once('dbupdate_3.4/28575_softwarebereitstellung.php'); require_once('dbupdate_3.4/41150_oe-pfad_db_view.php'); require_once('dbupdate_3.4/44031_stv_favorites.php'); +require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php b/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php new file mode 100644 index 000000000..ccc0a5827 --- /dev/null +++ b/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php @@ -0,0 +1,30 @@ +db_query("SELECT 1 FROM public.tbl_kontakttyp WHERE kontakttyp='email_unverifiziert'")) +{ + if($db->db_num_rows($result)==0) + { + $qry = "INSERT INTO public.tbl_kontakttyp(kontakttyp, beschreibung, bezeichnung_mehrsprachig) VALUES('email_unverifiziert', 'Unverifizierte E-Mail', '{\"Unverifizierte E-Mail\", \"Unverified email\"}');"; + + if(!$db->db_query($qry)) + echo 'Kontakttyp: '.$db->db_last_error().'
'; + else + echo '
Neuen Kontakttyp E-Mail unverifiziert in public.tbl_kontakttyp hinzugefügt'; + } +} + +// public.tbl_adressentyp: add type Meldeadresse +if($result = $db->db_query("SELECT 1 FROM public.tbl_adressentyp WHERE adressentyp_kurzbz='m'")) +{ + if($db->db_num_rows($result)==0) + { + $qry = "INSERT INTO public.tbl_adressentyp(adressentyp_kurzbz, bezeichnung, bezeichnung_mehrsprachig, sort) VALUES('m', 'Meldeadresse', '{\"Meldeadresse\", \"Registered adress\"}', 6);"; + + if(!$db->db_query($qry)) + echo 'Adressentyp: '.$db->db_last_error().'
'; + else + echo '
Neue Adressentyp Meldeadresse in public.tbl_adressentyp hinzugefügt'; + } +} From b8ff37eb8edfd28c897119d032c517533663574f Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Sat, 19 Oct 2024 17:25:09 +0200 Subject: [PATCH 002/312] added kontakt verifikation table for saving of kontakt verifikation data, added model for the table --- .../person/Kontaktverifikation_model.php | 42 ++++++++++++++++ ...14_electronic_onboarding_anbindung_ida.php | 48 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 application/models/person/Kontaktverifikation_model.php diff --git a/application/models/person/Kontaktverifikation_model.php b/application/models/person/Kontaktverifikation_model.php new file mode 100644 index 000000000..b15439edb --- /dev/null +++ b/application/models/person/Kontaktverifikation_model.php @@ -0,0 +1,42 @@ +dbTable = 'public.tbl_kontakt_verifikation'; + $this->pk = 'kontakt_verifikation_id'; + } + + /** + * Gets contact verification for a person and a verification code + * @param person_id + * @param kontakttyp + * @param verifikation_code + * @param expiration_days number of days after which verifikation code expires + * @return object success or error + */ + public function getKontaktVerifikation($person_id, $kontakttyp, $verifikation_code, $expiration_days = 1) + { + $qry = " + SELECT + kt.kontakt_id, + kv.verifikation_code + FROM + public.tbl_kontakt_verifikation kv + JOIN public.tbl_kontakt kt USING(kontakt_id) + WHERE kt.person_id = ? + AND kt.kontakttyp = ? + AND kv.verifikation_code = ? + AND kv.erstelldatum >= NOW() - INTERVAL '".$this->escape($expiration_days)." days' + ORDER BY + kt.kontakt_id DESC + LIMIT 1"; + + return $this->execQuery($qry, array($person_id, $kontakttyp, $verifikation_code)); + } +} diff --git a/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php b/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php index ccc0a5827..b204d45aa 100644 --- a/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php +++ b/system/dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php @@ -28,3 +28,51 @@ if($result = $db->db_query("SELECT 1 FROM public.tbl_adressentyp WHERE adressent echo '
Neue Adressentyp Meldeadresse in public.tbl_adressentyp hinzugefügt'; } } + +if (!$result = @$db->db_query('SELECT 1 FROM public.tbl_kontakt_verifikation LIMIT 1')) +{ + $qry = "CREATE SEQUENCE public.tbl_kontakt_verifikation_kontakt_verifikation_id_seq + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + START WITH 1 + CACHE 1 + NO CYCLE; + + CREATE TABLE public.tbl_kontakt_verifikation + ( + kontakt_verifikation_id integer DEFAULT nextval('public.tbl_kontakt_verifikation_kontakt_verifikation_id_seq'::regclass), + kontakt_id integer UNIQUE NOT NULL, + verifikation_code varchar(32) UNIQUE NOT NULL, + erstelldatum timestamp without time zone, + verifikation_datum timestamp without time zone, + app varchar(32), + CONSTRAINT pk_tbl_kontakt_verifikation_id PRIMARY KEY (kontakt_verifikation_id) + ); + + ALTER TABLE public.tbl_kontakt_verifikation ADD CONSTRAINT fk_tbl_kontakt_verifikation_kontakt_id FOREIGN KEY (kontakt_id) + REFERENCES public.tbl_kontakt (kontakt_id) + ON DELETE CASCADE ON UPDATE CASCADE; + + ALTER TABLE public.tbl_kontakt_verifikation ADD CONSTRAINT fk_tbl_kontakt_verifikation_app FOREIGN KEY (app) + REFERENCES system.tbl_app (app) + ON DELETE RESTRICT ON UPDATE CASCADE; + + COMMENT ON TABLE public.tbl_kontakt_verifikation IS 'Contact verification'; + COMMENT ON COLUMN public.tbl_kontakt_verifikation.kontakt_id IS 'Contact to verify'; + COMMENT ON COLUMN public.tbl_kontakt_verifikation.verifikation_code IS 'Code generated for verification'; + COMMENT ON COLUMN public.tbl_kontakt_verifikation.erstelldatum IS 'Time when verification code was generated'; + COMMENT ON COLUMN public.tbl_kontakt_verifikation.verifikation_datum IS 'Time when contact was verified'; + COMMENT ON COLUMN public.tbl_kontakt_verifikation.app IS 'App where contact was verified'; + + GRANT SELECT, UPDATE, INSERT, DELETE ON public.tbl_kontakt_verifikation TO web; + GRANT SELECT, UPDATE, INSERT, DELETE ON public.tbl_kontakt_verifikation TO vilesci; + GRANT SELECT, UPDATE ON public.tbl_kontakt_verifikation_kontakt_verifikation_id_seq TO vilesci; + GRANT SELECT, UPDATE ON public.tbl_kontakt_verifikation_kontakt_verifikation_id_seq TO web; + "; + + if(!$db->db_query($qry)) + echo 'public.tbl_kontakt_verifikation: '.$db->db_last_error().'
'; + else + echo ' public.tbl_kontakt_verifikation: Tabelle hinzugefuegt
'; +} From add9263d35ff2a1883c5336a5329144714118882 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 11 Nov 2024 17:17:33 +0100 Subject: [PATCH 003/312] added Electronic Onboarding phrases --- system/phrasesupdate.php | 366 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 364 insertions(+), 2 deletions(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 7ef0269c9..29d5d6132 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31025,8 +31025,370 @@ array( 'insertvon' => 'system' ) ) - ) - + ), + //**************************** FHC-Core-ElectronicOnboarding + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'emailFehlt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Email fehlt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Email is missing', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'emailUngueltig', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Email ist ungültig', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The email is not valid', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'emailRegistriert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Diese Email ist bereits registriert. Bitte verwenden sie eine andere Email oder loggen sie sich mit einer anderen Methode ein.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The email is already registered. Please choose a different email or use a different login method.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungZugangEmailBetreff', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zugang zu Ihrer Bewerbung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Access to your application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungZugangEmailAnredeWeiblich', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sehr geehrte Frau', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Dear Ms', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungZugangEmailAnredeMaennlich', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sehr geehrter Herr', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Dear Mr', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungZugangEmailAnredeNeutral', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sehr geehrte/r', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Dear', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'registrierungVerifzierenFuer', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Registrierung verifizieren für', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Verify registration for', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'registrierungVerifzieren', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Registrierung verifizieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Verify registration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'vorname', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Vorname', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'First name', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'nachname', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nachname', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Last name', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'geburtsdatum', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Geburtsdatum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Birth date', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'emailAdresse', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'E-Mail Adresse', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'E-mail address', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'emailGesendet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die E-Mail mit dem Link zu Ihrer Bewerbung wurde erfolgreich an {0} verschickt.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The email with the link to your application has been successfully sent to {0}.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'emailGesendetHinweis', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'In der Regel erhalten Sie das Mail in wenigen Minuten. Wenn Sie nach 24 Stunden noch kein Mail erhalten haben, + kontaktieren Sie bitte unsere {0}Studienberatung{1}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You should receive an e-mail within a few minutes. If you receive no e-mail within 24 hours please contact + our {0}student counselling team{1}.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'fehlerBeiRegistrierung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Fehler bei der Registrierung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error when registering', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'fehlerBeiRegistrierungText', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Da ist etwas schief gelaufen. Wir bitten um Entschuldigung.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Sorry, something went wrong.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'fehlerBeiRegistrierungNochmalVersuchen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nochmals versuchen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Try again', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), ); From b63146a8d89cdc4f08213350170346b1956cd560 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 20 Nov 2024 15:03:59 +0100 Subject: [PATCH 004/312] added MeldezettelJob for accepting Meldezettel of students with Meldeadresse --- .../controllers/jobs/MeldezettelJob.php | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 application/controllers/jobs/MeldezettelJob.php diff --git a/application/controllers/jobs/MeldezettelJob.php b/application/controllers/jobs/MeldezettelJob.php new file mode 100644 index 000000000..329597985 --- /dev/null +++ b/application/controllers/jobs/MeldezettelJob.php @@ -0,0 +1,86 @@ +_ci =& get_instance(); + + $this->_ci->load->model('crm/Dokumentprestudent_model', 'DokumentprestudentModel'); + } + + /** + * Sets Meldezettel to "accepted" for all students with Meldeadresse. + */ + public function acceptMeldezettel() + { + $this->logInfo('Start Meldezettel Job'); + + $params = array(self::DOKUMENT_KURZBZ); + + $qry = " + -- get all prestudents with meldeadresse, but no accepted Meldezettel + SELECT + DISTINCT prestudent_id + FROM + public.tbl_adresse + JOIN public.tbl_person USING (person_id) + JOIN public.tbl_prestudent ps USING (person_id) + WHERE + typ = 'm' + AND NOT EXISTS ( + SELECT + 1 + FROM + public.tbl_dokumentprestudent + WHERE + prestudent_id = ps.prestudent_id + AND dokument_kurzbz = ? + )"; + + // get all prestudents with Meldeadresse and no accpeted Meldezettel + $result = $this->_ci->DokumentprestudentModel->execReadOnlyQuery($qry, $params); + + if (isError($result)) + { + $this->logError(getError($result)); + } + + $count = 0; + + if (hasData($result)) + { + $prestudents = getData($result); + + foreach ($prestudents as $prestudent) + { + // set Meldezettel to accepted + $result = $this->_ci->DokumentprestudentModel->insert( + array( + 'prestudent_id' => $prestudent->prestudent_id, + 'dokument_kurzbz' => self::DOKUMENT_KURZBZ, + 'datum' => date('Y-m-d'), + 'insertamum' => strftime('%Y-%m-%d %H:%M'), + 'insertvon' => self::INSERT_VON + ) + ); + + if (isError($result)) + $this->logError(getError($result)); + else + $count++; + } + } + + $this->logInfo('End Meldezettel Job', array('Number of changes ' => $count)); + } +} From 0a97b39efbb877ff3f7a07ca0d531434b54cb3ca Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Tue, 24 Dec 2024 19:14:04 +0100 Subject: [PATCH 005/312] added phrases for electronic onboarding verification page --- .../person/Kontaktverifikation_model.php | 2 +- system/phrasesupdate.php | 137 +++++++++++++++++- 2 files changed, 132 insertions(+), 7 deletions(-) diff --git a/application/models/person/Kontaktverifikation_model.php b/application/models/person/Kontaktverifikation_model.php index b15439edb..17bcb1c35 100644 --- a/application/models/person/Kontaktverifikation_model.php +++ b/application/models/person/Kontaktverifikation_model.php @@ -29,7 +29,7 @@ class Kontaktverifikation_model extends DB_Model FROM public.tbl_kontakt_verifikation kv JOIN public.tbl_kontakt kt USING(kontakt_id) - WHERE kt.person_id = ? + WHERE kt.person_id = ? AND kt.kontakttyp = ? AND kv.verifikation_code = ? AND kv.erstelldatum >= NOW() - INTERVAL '".$this->escape($expiration_days)." days' diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 8d7bdd939..843a055a1 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31310,18 +31310,18 @@ array( array( 'app' => 'core', 'category' => 'onboarding', - 'phrase' => 'registrierungVerifzierenFuer', + 'phrase' => 'bewerbungVerifizierung', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Registrierung verifizieren für', + 'text' => 'Verifizierung Ihrer Bewerbung', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'Verify registration for', + 'text' => 'Application verification', 'description' => '', 'insertvon' => 'system' ) @@ -31330,18 +31330,143 @@ array( array( 'app' => 'core', 'category' => 'onboarding', - 'phrase' => 'registrierungVerifzieren', + 'phrase' => 'bewerbungVerifzieren', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Registrierung verifizieren', + 'text' => 'Bewerbung verifizieren', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'Verify registration', + 'text' => 'Verify application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungVerifizierungEinleitung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wenn Ihre Daten stimmen, geben Sie bitte Ihre E-Mail Adresse ein und drücken Sie auf "Bewerbung verfifizieren". +Danach erhalten Sie eine E-Mail mit dem Link zu Ihrer Bewerbung an die angegebene Adresse. +Dort können Sie Studienrichtungen hinzufügen, Ihre Daten vervollständigen, und sich unverbindlich bewerben.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'If your data is correct, please enter your email and click "Verify application". +We will then send you a link via e-mail to the address specified. There, you can add personal information or degree programs and submit non-binding applications.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungVerifizierungKontakthinweis', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wenn Sie mehr Informationen benötigen, steht Ihnen unsere Studienberatung gerne persönlich, telefonisch, per E-Mail oder WhatsApp zur Verfügung.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Should you require any additional information, please do not hesitate to contact our student counselling team in person, by phone, or via e-mail or WhatsApp.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungVerifizierungDatenschutzhinweis', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Datenschutz-Hinweis', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Privacy information', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungVerifizierungDatenschutzhinweisText', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die uns von Ihnen zum Zwecke der Bewerbung bekanntgegebenen Daten werden von uns ausschließlich zur Abwicklung der Bewerbung auf der Grundlage von vor- bzw vertraglichen Zwecken verarbeitet und mit der unten beschriebenen Ausnahme bei Unklarheiten betreffend die Zugangsvoraussetzungen nicht an Dritte weitergegeben. +Kommt es zu keinem weiteren Kontakt bzw zu keiner Aufnahme, löschen wir Ihre Daten nach drei Jahren.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The data communicated to us by you for the purpose of the application will be used by us exclusively for the processing of the application on the basis of pre-contractual or contractual purposes and will not be passed on to third parties with the exception described below in case of uncertainties regarding the entry requirements. +If there is no further contact or enrolment, your data will be deleted after three years.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungVerifizierungInformationenDatenschutzGrundverordnung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Informationen zu Ihren Betroffenenrechten finden Sie hier:', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Information on your data subject rights can be found here:', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bewerbungVerifizierungDatenschutzFragen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bei Fragen stehen wir Ihnen jederzeit unter folgender Mail zur Verfügung: ', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'If you have any questions, please contact us at ', 'description' => '', 'insertvon' => 'system' ) From 115b35ad7b40f3b27a161c5a8859ca019f1924e5 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 8 Jan 2025 14:52:32 +0100 Subject: [PATCH 006/312] electronic onboarding: changed phrase for error display --- system/phrasesupdate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 843a055a1..4de9cbb03 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31622,13 +31622,13 @@ If there is no further contact or enrolment, your data will be deleted after thr 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Da ist etwas schief gelaufen. Wir bitten um Entschuldigung.', + 'text' => 'Es ist ein Fehler bei der Registrierung aufgetreten.', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'Sorry, something went wrong.', + 'text' => 'An error occured during registration.', 'description' => '', 'insertvon' => 'system' ) From 08cb3d3809b34b43eb87558a02a5ae702e4c8681 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Thu, 16 Jan 2025 21:02:01 +0100 Subject: [PATCH 007/312] electronic onboarding phrase typo fix --- system/phrasesupdate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 4de9cbb03..7f030e62b 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31355,7 +31355,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Wenn Ihre Daten stimmen, geben Sie bitte Ihre E-Mail Adresse ein und drücken Sie auf "Bewerbung verfifizieren". + 'text' => 'Wenn Ihre Daten stimmen, geben Sie bitte Ihre E-Mail Adresse ein und drücken Sie auf "Bewerbung verifizieren". Danach erhalten Sie eine E-Mail mit dem Link zu Ihrer Bewerbung an die angegebene Adresse. Dort können Sie Studienrichtungen hinzufügen, Ihre Daten vervollständigen, und sich unverbindlich bewerben.', 'description' => '', From 39ada202774fbb77811a2cd54bd96f2a046917aa Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 22 Jan 2025 15:26:25 +0100 Subject: [PATCH 008/312] Electronic Onboarding: added phrases --- system/phrasesupdate.php | 102 +++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 20 deletions(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 7f030e62b..e177274ec 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31307,26 +31307,6 @@ array( ) ) ), - array( - 'app' => 'core', - 'category' => 'onboarding', - 'phrase' => 'bewerbungVerifizierung', - 'insertvon' => 'system', - 'phrases' => array( - array( - 'sprache' => 'German', - 'text' => 'Verifizierung Ihrer Bewerbung', - 'description' => '', - 'insertvon' => 'system' - ), - array( - 'sprache' => 'English', - 'text' => 'Application verification', - 'description' => '', - 'insertvon' => 'system' - ) - ) - ), array( 'app' => 'core', 'category' => 'onboarding', @@ -31654,6 +31634,88 @@ If there is no further contact or enrolment, your data will be deleted after thr ) ) ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'zustimmungDatenuebermittlung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Können in Ausnahmefällen die Zugangsvoraussetzungen von der FH Technikum Wien nicht abschließend abgeklärt werden, erteile ich die Zustimmung, dass die FH Technikum Wien die Dokumente zur Überprüfung an die zuständigen Behörden weiterleiten kann.
+Ich wurde darüber informiert, dass ich nicht verpflichtet bin, der Übermittlung meiner Daten zuzustimmen. Diese Zustimmung ist allerdings notwendig, um die Bewerbung berücksichtigen zu können.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'If in exceptional cases the admission requirements can not be finally clarified by the UAS Technikum Wien, I give my consent that the UAS Technikum Wien can forward the documents to the competent authorities for verification.
+I have been informed that I am under no obligation to consent to the transmission of my data. However, this consent is necessary in order for the application to be considered.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'zustimmungDatenschutzerklaerung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ich habe die Datenschutzerklärung zu Kenntnis genommen.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'I have taken note of the privacy policy.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bitteDatenuebermittlungZustimmen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sie müssen der Datenübermittlung zustimmen, um Ihre Bewerbung abschicken zu können.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You have to consent the transmission of your data to send the application.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'onboarding', + 'phrase' => 'bitteDatenschutzerklaerungZustimmen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sie müssen der Datenschutzerklärung zustimmen, um Ihre Bewerbung abschicken zu können.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You have to consent to the privacy statement to send the application.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ) ); From ea2be8026b463c8cf9c7132197aea82831aa9886 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Wed, 12 Mar 2025 08:06:23 +0100 Subject: [PATCH 009/312] filter hidden from kontakttyp, show typ.beschreibung in dropdown --- .../controllers/api/frontend/v1/stv/Kontakt.php | 15 ++++++++------- .../Details/Kontakt/Contact.js | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Kontakt.php b/application/controllers/api/frontend/v1/stv/Kontakt.php index fd16fff06..1c3c0c95d 100644 --- a/application/controllers/api/frontend/v1/stv/Kontakt.php +++ b/application/controllers/api/frontend/v1/stv/Kontakt.php @@ -439,6 +439,7 @@ class Kontakt extends FHCAPI_Controller $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); } $this->terminateWithSuccess((getData($result) ?: [])); + } public function getKontakttypen() @@ -446,13 +447,13 @@ class Kontakt extends FHCAPI_Controller $this->load->model('person/Kontakttyp_model', 'KontakttypModel'); $result = $this->KontakttypModel->load(); - if (isError($result)) { - $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - } - else - { - $this->terminateWithSuccess(getData($result) ?: []); - } + + $data = $this->getDataOrTerminateWithError($result); + + $filteredData = array_filter($data, function ($item) { + return $item->kontakttyp !== "hidden"; + }); + $this->terminateWithSuccess($filteredData); } public function loadContact() diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Kontakt/Contact.js b/public/js/components/Stv/Studentenverwaltung/Details/Kontakt/Contact.js index 87dc70eb1..0bda648a9 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Kontakt/Contact.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Kontakt/Contact.js @@ -322,7 +322,7 @@ export default{ v-model="contactData.kontakttyp"> > - + From 405062f5493fcdaaf3ed0e9b501a08e5aa93f081 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 7 May 2025 18:36:51 +0200 Subject: [PATCH 010/312] added Projektarbeit tab to Studierendenverwaltung, adding, editing, deleting of Projektarbeiten now possible --- .../api/frontend/v1/stv/Config.php | 5 + .../api/frontend/v1/stv/Projektarbeit.php | 334 +++++++ .../education/Lehrveranstaltung_model.php | 57 ++ .../models/education/Projektarbeit_model.php | 34 +- application/models/ressource/Firma_model.php | 21 +- public/js/api/factory/stv/projektarbeit.js | 80 ++ .../Details/Projektarbeit.js | 24 + .../Details/Projektarbeit/Betreuung.js | 847 ++++++++++++++++++ .../Details/Projektarbeit/Details.js | 461 ++++++++++ .../Details/Projektarbeit/Projektarbeit.js | 377 ++++++++ 10 files changed, 2230 insertions(+), 10 deletions(-) create mode 100644 application/controllers/api/frontend/v1/stv/Projektarbeit.php create mode 100644 public/js/api/factory/stv/projektarbeit.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 455b1e8fc..dd2eaed02 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -123,6 +123,11 @@ class Config extends FHCAPI_Controller 'config' => $config['finalexam'] ]; + $result['projektarbeit'] = [ + 'title' => $this->p->t('stv', 'tab_projektarbeit'), + 'component' => './Stv/Studentenverwaltung/Details/Projektarbeit.js' + ]; + $result['mobility'] = [ 'title' => $this->p->t('stv', 'tab_mobility'), 'component' => './Stv/Studentenverwaltung/Details/Mobility.js' diff --git a/application/controllers/api/frontend/v1/stv/Projektarbeit.php b/application/controllers/api/frontend/v1/stv/Projektarbeit.php new file mode 100644 index 000000000..3be2b26a3 --- /dev/null +++ b/application/controllers/api/frontend/v1/stv/Projektarbeit.php @@ -0,0 +1,334 @@ + ['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:rw', 'assistenz:rw'] + ]); + + // 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, firma_id, + lehrveranstaltung_id, lehreinheit_id, beginn, 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'); + $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'); + + $this->addMeta('form', $formData); + + if ($this->_validate($formData) == false) + { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + + $result = $this->ProjektarbeitModel->insert( + array_merge($formData, ['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()); + } + + $result = $this->ProjektarbeitModel->update( + $projektarbeit_id, + array_merge($formData, ['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( + array('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('projektarbeit', 'error_searchStringMissing', 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|numeric', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']), + //'matches' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']), + 'numeric' => $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']) + ]); + + return $this->form_validation->run(); + } + + /** + * + * @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; + } +} diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index 056fb45d7..446e6e9cf 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -988,4 +988,61 @@ class Lehrveranstaltung_model extends DB_Model return $this->execQuery($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); + } } diff --git a/application/models/education/Projektarbeit_model.php b/application/models/education/Projektarbeit_model.php index 109e23373..2be79f8e9 100644 --- a/application/models/education/Projektarbeit_model.php +++ b/application/models/education/Projektarbeit_model.php @@ -24,15 +24,17 @@ 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 - + lehre.tbl_projekttyp USING (projekttyp_kurzbz), lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, public.tbl_firma WHERE tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND + tbl_projektarbeit.firma_id = tbl_firma.firma_id AND tbl_projektarbeit.student_uid = ?"; $params = array($student_uid); @@ -69,4 +71,30 @@ class Projektarbeit_model extends DB_Model return $this->execQuery($qry, $params); } + + /** + * + * @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; + } } diff --git a/application/models/ressource/Firma_model.php b/application/models/ressource/Firma_model.php index 431f0815f..5ae53eeaf 100644 --- a/application/models/ressource/Firma_model.php +++ b/application/models/ressource/Firma_model.php @@ -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); } } diff --git a/public/js/api/factory/stv/projektarbeit.js b/public/js/api/factory/stv/projektarbeit.js new file mode 100644 index 000000000..243383de0 --- /dev/null +++ b/public/js/api/factory/stv/projektarbeit.js @@ -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 . + */ + +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 } + }; + } +}; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit.js new file mode 100644 index 000000000..3615e8f5d --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit.js @@ -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: ` +
+ +
` +}; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js new file mode 100644 index 000000000..5d223d694 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js @@ -0,0 +1,847 @@ +import {CoreFilterCmpt} from "../../../../filter/Filter.js"; +import FormForm from '../../../../Form/Form.js'; +import FormInput from '../../../../Form/Input.js'; +import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"; + +import ApiStvAbschlusspruefung from '../../../../../api/factory/stv/abschlusspruefung.js'; + +export default { + components: { + CoreFilterCmpt, + BsModal, + FormForm, + FormInput, + PvAutoComplete, + AbschlusspruefungDropdown, + PruefungList + }, + inject: { + cisRoot: { + from: 'cisRoot' + }, + config: { + from: 'config', + required: true + }, + $reloadList: { + from: '$reloadList', + required: true + }, + isBerechtigtDocAndOdt: { + from: 'hasPermissionOutputformat', + default: false + } + }, + computed: { + studentUids() { + if (this.student.uid) + { + return [this.student.uid]; + } + return this.student.map(e => e.uid); + }, + studentKzs(){ + if (this.student.uid) + { + return [this.student.studiengang_kz]; + } + return this.student.map(e => e.studiengang_kz); + }, + stg_kz(){ + return this.studentKzs[0]; + }, + }, + props: { + student: Object + }, + data() { + return { + tabulatorOptions: { + ajaxURL: 'dummy', + ajaxRequestFunc: () => this.$api.call(ApiStvAbschlusspruefung.getAbschlusspruefung(this.student.uid)), + ajaxResponse: (url, params, response) => response.data, + columns: [ + {title: "vorsitz", field: "vorsitz_nachname"}, + {title: "abschlussbeurteilung", field: "beurteilung_bezeichnung"}, + {title: "prueferIn1", field: "p1_nachname", visible: false}, + {title: "prueferIn2", field: "p2_nachname", visible: false}, + {title: "prueferIn3", field: "p3_nachname", visible: false}, + { + title: "datum", + field: "datum", + 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: "uhrzeit", field: "uhrzeit"}, + { + title: "freigabe", + field: "freigabedatum", + 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: "pruefungsantritt", field: "antritt_bezeichnung"}, + { + title: "sponsion", + field: "sponsion", + 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: "anmerkung", field: "anmerkung"}, + {title: "abschlusspruefung_id", field: "abschlusspruefung_id", visible: false}, + {title: "typ", field: "pruefungstyp_kurzbz", 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 = ''; + button.title = this.$p.t('ui', 'bearbeiten'); + button.addEventListener('click', (event) => + this.actionEditAbschlusspruefung(cell.getData().abschlusspruefung_id) + ); + container.append(button); + + button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'loeschen'); + button.addEventListener('click', () => + this.actionDeleteAbschlusspruefung(cell.getData().abschlusspruefung_id) + ); + container.append(button); + + container.append(cell.getData().actionDiv); + + return container; + }, + frozen: true + }, + ], + layout: 'fitDataFill', + layoutColumnsOnNewData: false, + height: 'auto', + minHeight: '200', + selectable: true, + index: 'abschlusspruefung_id', + persistenceID: 'stv-details-finalexam' + }, + 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', 'abschlusspruefung', 'ui']); + + + let cm = this.$refs.table.tabulator.columnManager; + + cm.getColumnByField('vorsitz_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'vorsitz_header') + }); + cm.getColumnByField('beurteilung_bezeichnung').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'abschlussbeurteilung') + }); + cm.getColumnByField('p1_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefer1') + }); + cm.getColumnByField('p2_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefer2') + }); + cm.getColumnByField('p3_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefer3') + }); + cm.getColumnByField('datum').component.updateDefinition({ + title: this.$p.t('global', 'datum') + }); + cm.getColumnByField('uhrzeit').component.updateDefinition({ + title: this.$p.t('global', 'uhrzeit') + }); + cm.getColumnByField('freigabedatum').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'freigabe') + }); + cm.getColumnByField('antritt_bezeichnung').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefungsantritt') + }); + cm.getColumnByField('sponsion').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'sponsion') + }); + cm.getColumnByField('anmerkung').component.updateDefinition({ + title: this.$p.t('global', 'anmerkung') + }); + cm.getColumnByField('pruefungstyp_kurzbz').component.updateDefinition({ + title: this.$p.t('global', 'typ') + }); + cm.getColumnByField('abschlusspruefung_id').component.updateDefinition({ + title: this.$p.t('ui', 'abschlusspruefung_id') + }); + /* + cm.getColumnByField('actions').component.updateDefinition({ + title: this.$p.t('global', 'aktionen') + }); + */ + } + } + ], + tabulatorData: [], + lastSelected: null, + formData: { + typStg: null, + pruefungstyp_kurzbz: null, + akadgrad_id: null, + vorsitz: null, + pruefungsantritt_kurzbz: null, + abschlussbeurteilung_kurzbz: null, + datum: null, + sponsion: null, + pruefer1: null, + pruefer2: null, + pruefer3: null, + anmerkung: null, + protokoll: null, + note: null, + link: null + }, + statusNew: true, + arrTypen: [], + arrAntritte: [], + arrBeurteilungen: [], + arrAkadGrad: [], + arrNoten: [], + filteredMitarbeiter: [], + filteredPruefer: [], + abortController: { + mitarbeiter: null, + pruefer: null + }, + stgInfo: { typ: '', oe_kurzbz: '' } + } + }, + watch: { + student(){ + if (this.$refs.table) { + this.$refs.table.reloadTable(); + } + this.getStudiengangByKz(); + } + }, + methods: { + getStudiengangByKz(){ + this.stgInfo = { typ: '', oe_kurzbz: '' }; + this.$api + .call(ApiStudiengang.getStudiengangByKz(this.stg_kz)) + .then(result => this.stgInfo = result.data) + .catch(this.$fhcAlert.handleSystemError); + }, + actionNewAbschlusspruefung() { + this.resetForm(); + this.statusNew = true; + this.$refs.finalexamModal.show(); + this.setDefaultFormData(); + }, + actionEditAbschlusspruefung(abschlusspruefung_id) { + this.resetForm(); + this.statusNew = false; + this.$refs.finalexamModal.show(); + this.loadAbschlusspruefung(abschlusspruefung_id); + }, + actionDeleteAbschlusspruefung(abschlusspruefung_id) { + this.$fhcAlert + .confirmDelete() + .then(result => result + ? abschlusspruefung_id + : Promise.reject({handled: true})) + .then(this.deleteAbschlusspruefung) + .catch(this.$fhcAlert.handleSystemError); + }, + addNewAbschlusspruefung() { + const dataToSend = { + uid: this.student.uid, + formData: this.formData + }; + + return this.$refs.formFinalExam + .call(ApiStvAbschlusspruefung.addNewAbschlusspruefung(dataToSend)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + this.hideModal('finalexamModal'); + this.resetForm(); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.reload(); + }); + }, + hideModal(modalRef){ + this.$refs[modalRef].hide(); + }, + reload() { + this.$refs.table.reloadTable(); + }, + loadAbschlusspruefung(abschlusspruefung_id) { + return this.$api + .call(ApiStvAbschlusspruefung.loadAbschlusspruefung(abschlusspruefung_id)) + .then(result => { + this.formData = result.data; + //TODO(Manu) check if cisRoot is okay + this.formData.link = this.cisRoot + 'index.ci.php/lehre/Pruefungsprotokoll/showProtokoll?abschlusspruefung_id=' + this.formData.abschlusspruefung_id + '&fhc_controller_id=67481e5ed5490'; + return result; + }) + .catch(this.$fhcAlert.handleSystemError); + }, + updateAbschlusspruefung(abschlusspruefung_id) { + const dataToSend = { + id: abschlusspruefung_id, + formData: this.formData + }; + return this.$refs.formFinalExam + .call(ApiStvAbschlusspruefung.updateAbschlusspruefung(dataToSend)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + this.hideModal('finalexamModal'); + this.resetForm(); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.reload(); + }); + }, + deleteAbschlusspruefung(abschlusspruefung_id) { + return this.$api + .call(ApiStvAbschlusspruefung.deleteAbschlusspruefung(abschlusspruefung_id)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.reload(); + }); + }, + resetForm() { + this.formData.pruefungstyp_kurzbz = null; + this.formData.akadgrad_id = null; + this.formData.vorsitz = null; + this.formData.pruefungsantritt_kurzbz = null; + this.formData.abschlussbeurteilung_kurzbz = null; + this.formData.datum = null; //oder new Date(); + this.formData.sponsion = null; + this.formData.pruefer1 = null; + this.formData.pruefer2 = null; + this.formData.pruefer3 = null; + this.formData.anmerkung = null; + this.formData.protokoll = null; + this.formData.note = null; + this.formData.p1 = null; + this.formData.p2 = null; + this.formData.p3 = null; + this.formData.pv = null; + }, + search(event) { + if (this.abortController.mitarbeiter) { + this.abortController.mitarbeiter.abort(); + } + this.abortController.mitarbeiter = new AbortController(); + + return this.$api + .call(ApiStvAbschlusspruefung.getMitarbeiter(event.query)) + .then(result => { + this.filteredMitarbeiter = result.data.retval; + }); + }, + searchNotAkad(event) { + if (this.abortController.pruefer) { + this.abortController.pruefer.abort(); + } + this.abortController.pruefer = new AbortController(); + + return this.$api + .call(ApiStvAbschlusspruefung.getPruefer(event.query)) + .then(result => { + this.filteredPruefer = result.data.retval; + }); + }, + setDefaultFormData() { + + this.resetForm(); + + if (this.stgInfo.typ === 'b') { + this.formData.pruefungstyp_kurzbz = 'Bachelor'; + this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster'); + } + if (this.stgInfo.typ === 'd' || this.stgInfo === 'm') { + this.formData.pruefungstyp_kurzbz = 'Diplom'; + this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster'); + } + if (this.stgInfo.typ === 'lg') { + this.formData.pruefungstyp_kurzbz = 'lgabschluss'; + } + + if (!this.formData.akadgrad_id && this.arrAkadGrad.length > 0) { + this.formData.akadgrad_id = this.arrAkadGrad[0].akadgrad_id; + } + }, + printDocument(link) { + window.open(link, '_blank'); + }, + }, + created() { + this.$api + .call(ApiStvAbschlusspruefung.getTypenAbschlusspruefung()) + .then(result => { + this.arrTypen = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiStvAbschlusspruefung.getTypenAntritte()) + .then(result => { + this.arrAntritte = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiStvAbschlusspruefung.getBeurteilungen()) + .then(result => { + this.arrBeurteilungen = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiStvAbschlusspruefung.getNoten()) + .then(result => { + this.arrNoten = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiStvAbschlusspruefung.getAkadGrade(this.student.studiengang_kz)) + .then(result => { + this.arrAkadGrad = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + if (!this.student.length) { + this.$api + .call(ApiStudiengang.getStudiengangByKz(this.student.studiengang_kz)) + .then(result => { + this.stgInfo = result.data; + this.setDefaultFormData(); + }) + .catch(this.$fhcAlert.handleSystemError); + } else + this.getStudiengangByKz(); + }, + template: ` +
+

{{this.$p.t('stv','tab_finalexam')}}

+ +
+ +
+ + + + + + + + + + + + + {{this.$p.t('global','details')}} +

[{{$p.t('ui', 'neu')}}]

+
+ + + + + + + +
+ +
+ + + + +
+ +
+ + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+
+

{{$p.t('abschlusspruefung', 'zurBeurteilung')}}

+
+ +
+ +
+ + + +
+ + + + +
+` +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js new file mode 100644 index 000000000..a6256ceb1 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js @@ -0,0 +1,461 @@ +import {CoreFilterCmpt} from "../../../../filter/Filter.js"; +import FormForm from '../../../../Form/Form.js'; +import FormInput from '../../../../Form/Input.js'; +import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"; + +import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js'; + +export default { + components: { + CoreFilterCmpt, + FormForm, + FormInput, + PvAutoComplete + }, + emits: ['details-saved'], + inject: { + defaultSemester: { + from: 'defaultSemester' + } + //~ cisRoot: { + //~ from: 'cisRoot' + //~ }, + //~ config: { + //~ from: 'config', + //~ required: true + //~ } + }, + computed: { + 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; + }, + //~ preparedFormData() { + //~ console.log("FOR DATA CALLEd"); + //~ if (this.formData.projektarbeit_id == null) delete(this.formData.projektarbeit_id); + //~ if (this.formData.firma) this.formData.firma_id = this.formData.firma.firma_id; + //~ delete(this.formData.firma); + //~ delete(this.formData.firma_name); + //~ delete(this.formData.lehrveranstaltung_id); + //~ // convert null value fields from string to null + //~ return this.formData; + //~ } + }, + props: { + statusNew: Boolean, + student: Object, + projektarbeit: Object, + stg_kz: Number + }, + 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 + } + } + }, + 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; + }, + getFormData(statusNew, studiensemester_kurzbz, additional_lehrveranstaltung_id/*, successCallback*/) { + + //~ let callArray = [ + //~ this.$api.call(ApiStvProjektarbeit.getTypenProjektarbeit()), + //~ this.$api.call(ApiStvProjektarbeit.getLehrveranstaltungen( + //~ this.student.uid, + //~ projektarbeit_id ? null : this.student.studiengang_kz, + //~ studiensemester_kurzbz ?? this.defaultSemester, + //~ additional_lehrveranstaltung_id ?? null + //~ )), + //~ this.$api.call(ApiStvProjektarbeit.getNoten()) + //~ ]; + + //~ if (projektarbeit_id) callArray.push(this.$api.call(ApiStvProjektarbeit.loadProjektarbeit(projektarbeit_id))); + + //~ // Run when All promises are settled + //~ Promise.allSettled(callArray).then((results) => { + //~ let hasError = false; + //~ let allFormData = []; + //~ results.forEach((promise_result) => { + + //~ if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") { + //~ allFormData.push(promise_result.value.data); + //~ } else { + //~ hasError = true; + //~ //this.$fhcAlert.handleSystemError(promise_result); + //~ } + //~ //let data = promise_result.value.data; + //~ console.log(promise_result.status); + //~ }); + + //~ if (!hasError) { + //~ this.setFormData(allFormData[0], allFormData[1], allFormData[2], allFormData[3], allFormData[4] ?? null); + //~ if (successCallback) successCallback(); + //~ } + //~ }); + + 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); + }, + setFormData(arrTypen, arrLvs, arrNoten, projektarbeitData) { + this.arrTypen = arrTypen; + this.arrLvs = arrLvs; + this.arrNoten = arrNoten; + if (projektarbeitData) { + projektarbeitData.firma = {firma_id: projektarbeitData.firma_id, name: projektarbeitData.firma_name}; + this.formData = projektarbeitData; + } + }, + loadProjektarbeit(projektarbeit_id) { + + return this.$api + .call(ApiStvProjektarbeit.loadProjektarbeit(projektarbeit_id)) + .then(result => { + this.formData = result.data; + 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; + }, + getPreparedFormData() { + let preparedFormData = JSON.parse(JSON.stringify(this.formData)); // deep copy + + // delete "helper" fields + if (preparedFormData.projektarbeit_id == null) delete(preparedFormData.projektarbeit_id); + if (preparedFormData.firma) preparedFormData.firma_id = preparedFormData.firma.firma_id; + delete(preparedFormData.firma); + delete(preparedFormData.firma_name); + delete(preparedFormData.lehrveranstaltung_id); + + return preparedFormData; + } + //~ setDefaultFormData() { + //~ this.resetForm(); + //~ }, + }, + template: ` + + + {{this.$p.t('global','details')}} +

[{{$p.t('ui', 'neu')}}]

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+ + +
+ + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + +
+ + +
+
+ +
+ + + + +
+ + +
+
+ +
+ + +
+ +
` +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js new file mode 100644 index 000000000..034b3fd6c --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -0,0 +1,377 @@ +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"; + +export default { + components: { + CoreFilterCmpt, + BsModal, + FormForm, + FormInput, + PvAutoComplete, + ProjektarbeitDetails + }, + inject: { + cisRoot: { + from: 'cisRoot' + }, + config: { + from: 'config', + required: true + }, + $reloadList: { + from: '$reloadList', + required: true + }, + isBerechtigtDocAndOdt: { + from: 'hasPermissionOutputformat', + default: false + } + }, + computed: { + //~ studentUids() { + //~ if (this.student.uid) + //~ { + //~ return [this.student.uid]; + //~ } + //~ return this.student.map(e => e.uid); + //~ }, + studentKzs(){ + if (this.student.uid) + { + return [this.student.studiengang_kz]; + } + return this.student.map(e => e.studiengang_kz); + }, + stg_kz(){ + return this.studentKzs.length > 0 ? this.studentKzs.length[0] : null; + } + }, + props: { + student: Object + }, + data() { + return { + tabulatorOptions: { + 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: "projekttyp_kurzbz"}, + {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: '', + crossElement: '' + }, + 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: '', + crossElement: '' + }, + 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 = ''; + button.title = this.$p.t('ui', 'bearbeiten'); + button.addEventListener('click', (event) => { + let data = cell.getData(); + this.actionEditProjektarbeit(data.projektarbeit_id, data.studiensemester_kurzbz, data.lehrveranstaltung_id); + }); + container.append(button); + + button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'loeschen'); + button.addEventListener('click', () => + this.actionDeleteProjektarbeit(cell.getData().projektarbeit_id) + ); + container.append(button); + + container.append(cell.getData().actionDiv); + + return container; + }, + frozen: true + }, + ], + layout: 'fitDataFill', + layoutColumnsOnNewData: false, + height: 'auto', + minHeight: '200', + selectable: 1, + index: 'projektarbeit_id', + persistenceID: 'stv-details-projektarbeit' + }, + tabulatorEvents: [ + { + event: 'rowSelectionChanged', + handler: this.rowSelectionChanged + }, + { + 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']); + + + let cm = this.$refs.table.tabulator.columnManager; + + //~ cm.getColumnByField('vorsitz_nachname').component.updateDefinition({ + //~ title: this.$p.t('abschlusspruefung', 'vorsitz_header') + //~ }); + /* + cm.getColumnByField('actions').component.updateDefinition({ + title: this.$p.t('global', 'aktionen') + }); + */ + } + }, + ], + tabulatorData: [], + lastSelected: null, + statusNew: true, + studiensemester_kurzbz: null, + lehrveranstaltung_id: null + } + }, + //~ watch: { + //~ student(){ + //~ if (this.$refs.table) { + //~ this.$refs.table.reloadTable(); + //~ } + //~ this.getStudiengangByKz(); + //~ } + //~ }, + methods: { + actionNewProjektarbeit() { + this.statusNew = true; + this.$refs.projektarbeitDetails.resetForm(); + this.$refs.projektarbeitDetails.getFormData(); + this.$refs.projektarbeitModal.show(); + }, + actionEditProjektarbeit(projektarbeit_id, studiensemester_kurzbz, lehrveranstaltung_id) { + this.statusNew = false; + this.$refs.projektarbeitDetails.getFormData(this.statusNew, studiensemester_kurzbz, lehrveranstaltung_id); + this.$refs.projektarbeitDetails.loadProjektarbeit(projektarbeit_id); + 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() { + Promise.allSettled([ + this.$refs.projektarbeitDetails.addNewProjektarbeit() + ]).then((results) => { + let hasError = false; + results.forEach((promise_result) => { + + if (!(promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success")) { + + hasError = true; + } + }); + + if (!hasError) { + this.projektarbeitSaved(); + } + }); + }, + updateProjektarbeit() { + Promise.allSettled( + [ + this.$refs.projektarbeitDetails.updateProjektarbeit() + ]).then((results) => { + let hasError = false; + results.forEach((promise_result) => { + + if (!(promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success")) { + + hasError = true; + } + }); + + if (!hasError) { + this.projektarbeitSaved(); + } + }); + }, + 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'); + this.$refs.projektarbeitDetails.resetForm(); + }, + rowSelectionChanged(data) { + console.log("selection changed"); + this.lastSelected = data.length > 0 ? data[0] : null; + }, + hideModal(modalRef){ + this.$refs[modalRef].hide(); + }, + reload() { + this.$refs.table.reloadTable(); + } + }, + created() { + }, + template: ` +
+

{{this.$p.t('stv','tab_projektarbeit')}}

+ + + + + + + + + + + + + +
+` +} + From 382006aa8bfdec7696256d2b2236eb448003daf7 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Fri, 16 May 2025 13:37:21 +0200 Subject: [PATCH 011/312] Projektarbeit Studierendenverwaltung: now possible to save and edit Projektbetreuer --- application/config/stv.php | 7 +- .../api/frontend/v1/stv/Config.php | 5 +- .../api/frontend/v1/stv/Projektarbeit.php | 11 +- .../api/frontend/v1/stv/Projektbetreuer.php | 333 +++++++ .../models/education/Projektarbeit_model.php | 9 +- .../education/Projektbetreuer_model.php | 1 + application/models/person/Person_model.php | 19 +- .../models/ressource/Stundensatz_model.php | 95 +- public/js/api/factory/stv/projektbetreuer.js | 73 ++ .../Details/Projektarbeit/Betreuung.js | 847 ------------------ .../Details/Projektarbeit/Details.js | 27 +- .../Details/Projektarbeit/Projektarbeit.js | 86 +- .../Details/Projektarbeit/Projektbetreuer.js | 421 +++++++++ system/phrasesupdate.php | 2 +- 14 files changed, 1002 insertions(+), 934 deletions(-) create mode 100644 application/controllers/api/frontend/v1/stv/Projektbetreuer.php create mode 100644 public/js/api/factory/stv/projektbetreuer.js delete mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js diff --git a/application/config/stv.php b/application/config/stv.php index 31ce3f521..f5c1599ab 100644 --- a/application/config/stv.php +++ b/application/config/stv.php @@ -54,7 +54,7 @@ $config['tabs'] = ], ] ]; - + // List of fields to show when ZGV_DOKTOR_ANZEIGEN is defined $fieldsZgvDoktor = ['zgvdoktorort', 'zgvdoktordatum', 'zgvdoktornation', 'zgvdoktor_erfuellt', 'zgvdoktor_code']; @@ -74,3 +74,8 @@ if (!defined('ZGV_DOKTOR_ANZEIGEN') || !ZGV_DOKTOR_ANZEIGEN) { $fieldsZgvDoktor ); } + +$config['tabs']['projektarbeit']['defaultProjektbetreuerStunden'] = '4.0'; +$config['tabs']['projektarbeit']['defaultProjektbetreuerStundenDiplom'] = '5.0'; +$config['tabs']['projektarbeit']['lvLektroinnenzuteilungFixangestelltStundensatz'] = true; +$config['tabs']['projektarbeit']['defaultProjektbetreuerStundensatz'] = '80.0'; diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index dd2eaed02..afdf8efb8 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -125,7 +125,8 @@ class Config extends FHCAPI_Controller $result['projektarbeit'] = [ 'title' => $this->p->t('stv', 'tab_projektarbeit'), - 'component' => './Stv/Studentenverwaltung/Details/Projektarbeit.js' + 'component' => './Stv/Studentenverwaltung/Details/Projektarbeit.js', + 'config' => $config['projektarbeit'] ]; $result['mobility'] = [ @@ -323,7 +324,7 @@ class Config extends FHCAPI_Controller $title_eng = $this->p->t("global", "englisch"); $title_ff = $this->p->t("stv", "document_certificate"); $title_lv = $this->p->t("stv", "document_coursecertificate"); - + $link_ff = "documents/export/" . "zertifikat.rdf.php/" . "Zertifikat" . diff --git a/application/controllers/api/frontend/v1/stv/Projektarbeit.php b/application/controllers/api/frontend/v1/stv/Projektarbeit.php index 3be2b26a3..70775fdbc 100644 --- a/application/controllers/api/frontend/v1/stv/Projektarbeit.php +++ b/application/controllers/api/frontend/v1/stv/Projektarbeit.php @@ -17,7 +17,7 @@ class Projektarbeit extends FHCAPI_Controller 'getTypenProjektarbeit' => ['admin:r', 'assistenz:r'], 'getFirmen' => ['admin:r', 'assistenz:r'], 'getLehrveranstaltungen' => ['admin:r', 'assistenz:r'], - 'getNoten' => ['admin:rw', 'assistenz:rw'] + 'getNoten' => ['admin:r', 'assistenz:r'] ]); // Load Libraries @@ -91,7 +91,7 @@ class Projektarbeit extends FHCAPI_Controller ); $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'); + $this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT'); $result = $this->ProjektarbeitModel->loadWhere( array('projektarbeit_id' => $projektarbeit_id) ); @@ -170,7 +170,7 @@ class Projektarbeit extends FHCAPI_Controller if (isError($validate)) return $this->terminateWithError(getError($validate), self::ERROR_TYPE_GENERAL); $result = $this->ProjektarbeitModel->delete( - array('projektarbeit_id' => $projektarbeit_id) + ['projektarbeit_id' => $projektarbeit_id] ); if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); @@ -273,10 +273,9 @@ class Projektarbeit extends FHCAPI_Controller 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Projekttyp']) ]); - $this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'required|numeric', [ + $this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'required|is_natural', [ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']), - //'matches' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']), - 'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Lehreinheit']) + 'is_natural' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Lehreinheit']) ]); $this->form_validation->set_rules('beginn', 'Beginn', 'is_valid_date', [ diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php new file mode 100644 index 000000000..ac6663132 --- /dev/null +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -0,0 +1,333 @@ + ['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'], + '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, 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; + //~ } + //~ } + + $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('projektbetreuerListe'); + + //$this->addMeta('form', $projektbetreuer); + + if (!is_array($projektbetreuer)) + { + return $this->terminateWithError( + $this->p->t('projektarbeit', 'error_invalidProjektbetreuer'), self::ERROR_TYPE_GENERAL + ); + } + + foreach ($projektbetreuer as $pb) + { + if ($this->_validate($pb) == false) + { + $this->addMeta('test', 'foisch'); + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + } + + $result = null; + + foreach ($projektbetreuer as $pb) + { + //~ $this->addMeta('form', $pb); + //~ error_log(print_r($pb, true)); + //die(); + + //~ $this->ProjektbetreuerModel->addSelect('1'); + //~ $checkResult = $this->ProjektbetreuerModel->loadWhere( + //~ ['person_id' => $pb['person_id'], 'projektarbeit_id' => $projektarbeit_id, 'betreuerart_kurzbz' => $pb['betreuerart_kurzbz']] + //~ ); + + //~ if (isError($checkResult)) $this->terminateWithError(getError($checkResult), self::ERROR_TYPE_GENERAL); + + $betreuer = [ + 'projektarbeit_id' => $projektarbeit_id, + 'person_id' => $pb['person_id'], + 'note' => $pb['note'], + 'stunden' => $pb['stunden'], + 'stundensatz' => $pb['stundensatz'], + 'betreuerart_kurzbz' => $pb['betreuerart_kurzbz'] + ]; + + if (isset($pb['person_id_old']) && isset($pb['betreuerart_kurzbz_old'])) + { + $result = $this->ProjektbetreuerModel->update( + [ + 'projektarbeit_id' => $projektarbeit_id, + 'person_id' => $pb['person_id_old'], + 'betreuerart_kurzbz' => $pb['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'=> 'Projektbetreuer 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'=> '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); + + $this->addMeta('res', $result); + + //if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + + return $this->terminateWithSuccess($result); + } + + public function getProjektbetreuerBySearchQuery() + { + $searchString = $this->input->get('searchString'); + + if (!isset($searchString)) $this->terminateWithError($this->p->t('projektarbeit', 'error_searchStringMissing', self::ERROR_TYPE_GENERAL)); + + $result = $this->PersonModel->searchPerson($searchString); + + $this->addMeta('met', $result); + + if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + + return $this->terminateWithSuccess(hasData($result) ? $this->_addFullNameToBetreuer(getData($result)) : []); + } + + /** + * + * @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' => 'Betreuerart']) + ]); + + $this->form_validation->set_rules('person_id', 'Person', 'required', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Person ID']) + ]); + + $this->form_validation->set_rules('stunden', 'Stunden', 'numeric', [ + 'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Stunden']) + ]); + + $this->form_validation->set_rules('stundensatz', 'Stundensatz', 'numeric', [ + 'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => '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; + } +} diff --git a/application/models/education/Projektarbeit_model.php b/application/models/education/Projektarbeit_model.php index 2be79f8e9..357886de1 100644 --- a/application/models/education/Projektarbeit_model.php +++ b/application/models/education/Projektarbeit_model.php @@ -29,12 +29,11 @@ class Projektarbeit_model extends DB_Model tbl_firma.name AS firma_name FROM lehre.tbl_projektarbeit - JOIN - lehre.tbl_projekttyp USING (projekttyp_kurzbz), lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, public.tbl_firma + 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.firma_id = tbl_firma.firma_id AND tbl_projektarbeit.student_uid = ?"; $params = array($student_uid); diff --git a/application/models/education/Projektbetreuer_model.php b/application/models/education/Projektbetreuer_model.php index 95950bf95..02368ae21 100644 --- a/application/models/education/Projektbetreuer_model.php +++ b/application/models/education/Projektbetreuer_model.php @@ -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; } /** diff --git a/application/models/person/Person_model.php b/application/models/person/Person_model.php index 997048972..d955f6401 100644 --- a/application/models/person/Person_model.php +++ b/application/models/person/Person_model.php @@ -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; @@ -423,4 +432,4 @@ class Person_model extends DB_Model return success($result); } } -} \ No newline at end of file +} diff --git a/application/models/ressource/Stundensatz_model.php b/application/models/ressource/Stundensatz_model.php index 10f5a6aa1..9d41dfbd9 100644 --- a/application/models/ressource/Stundensatz_model.php +++ b/application/models/ressource/Stundensatz_model.php @@ -2,7 +2,7 @@ class Stundensatz_model extends DB_Model { - + /** * Constructor */ @@ -42,4 +42,95 @@ class Stundensatz_model extends DB_Model return $this->execQuery($qry, $params); } -} \ No newline at end of file + + 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; + } +} diff --git a/public/js/api/factory/stv/projektbetreuer.js b/public/js/api/factory/stv/projektbetreuer.js new file mode 100644 index 000000000..fc710fe81 --- /dev/null +++ b/public/js/api/factory/stv/projektbetreuer.js @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2025 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + 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, projektbetreuerListe) { + return { + method: 'post', + url: 'api/frontend/v1/stv/projektbetreuer/saveProjektbetreuer', + params: { projektarbeit_id, projektbetreuerListe } + }; + }, + 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 } + }; + }, + validateProjektbetreuer(projektbetreuer) { + return { + method: 'post', + url: 'api/frontend/v1/stv/projektbetreuer/validateProjektbetreuer', + params: { projektbetreuer } + }; + } +}; diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js deleted file mode 100644 index 5d223d694..000000000 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Betreuung.js +++ /dev/null @@ -1,847 +0,0 @@ -import {CoreFilterCmpt} from "../../../../filter/Filter.js"; -import FormForm from '../../../../Form/Form.js'; -import FormInput from '../../../../Form/Input.js'; -import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"; - -import ApiStvAbschlusspruefung from '../../../../../api/factory/stv/abschlusspruefung.js'; - -export default { - components: { - CoreFilterCmpt, - BsModal, - FormForm, - FormInput, - PvAutoComplete, - AbschlusspruefungDropdown, - PruefungList - }, - inject: { - cisRoot: { - from: 'cisRoot' - }, - config: { - from: 'config', - required: true - }, - $reloadList: { - from: '$reloadList', - required: true - }, - isBerechtigtDocAndOdt: { - from: 'hasPermissionOutputformat', - default: false - } - }, - computed: { - studentUids() { - if (this.student.uid) - { - return [this.student.uid]; - } - return this.student.map(e => e.uid); - }, - studentKzs(){ - if (this.student.uid) - { - return [this.student.studiengang_kz]; - } - return this.student.map(e => e.studiengang_kz); - }, - stg_kz(){ - return this.studentKzs[0]; - }, - }, - props: { - student: Object - }, - data() { - return { - tabulatorOptions: { - ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvAbschlusspruefung.getAbschlusspruefung(this.student.uid)), - ajaxResponse: (url, params, response) => response.data, - columns: [ - {title: "vorsitz", field: "vorsitz_nachname"}, - {title: "abschlussbeurteilung", field: "beurteilung_bezeichnung"}, - {title: "prueferIn1", field: "p1_nachname", visible: false}, - {title: "prueferIn2", field: "p2_nachname", visible: false}, - {title: "prueferIn3", field: "p3_nachname", visible: false}, - { - title: "datum", - field: "datum", - 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: "uhrzeit", field: "uhrzeit"}, - { - title: "freigabe", - field: "freigabedatum", - 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: "pruefungsantritt", field: "antritt_bezeichnung"}, - { - title: "sponsion", - field: "sponsion", - 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: "anmerkung", field: "anmerkung"}, - {title: "abschlusspruefung_id", field: "abschlusspruefung_id", visible: false}, - {title: "typ", field: "pruefungstyp_kurzbz", 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 = ''; - button.title = this.$p.t('ui', 'bearbeiten'); - button.addEventListener('click', (event) => - this.actionEditAbschlusspruefung(cell.getData().abschlusspruefung_id) - ); - container.append(button); - - button = document.createElement('button'); - button.className = 'btn btn-outline-secondary btn-action'; - button.innerHTML = ''; - button.title = this.$p.t('ui', 'loeschen'); - button.addEventListener('click', () => - this.actionDeleteAbschlusspruefung(cell.getData().abschlusspruefung_id) - ); - container.append(button); - - container.append(cell.getData().actionDiv); - - return container; - }, - frozen: true - }, - ], - layout: 'fitDataFill', - layoutColumnsOnNewData: false, - height: 'auto', - minHeight: '200', - selectable: true, - index: 'abschlusspruefung_id', - persistenceID: 'stv-details-finalexam' - }, - 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', 'abschlusspruefung', 'ui']); - - - let cm = this.$refs.table.tabulator.columnManager; - - cm.getColumnByField('vorsitz_nachname').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'vorsitz_header') - }); - cm.getColumnByField('beurteilung_bezeichnung').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'abschlussbeurteilung') - }); - cm.getColumnByField('p1_nachname').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'pruefer1') - }); - cm.getColumnByField('p2_nachname').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'pruefer2') - }); - cm.getColumnByField('p3_nachname').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'pruefer3') - }); - cm.getColumnByField('datum').component.updateDefinition({ - title: this.$p.t('global', 'datum') - }); - cm.getColumnByField('uhrzeit').component.updateDefinition({ - title: this.$p.t('global', 'uhrzeit') - }); - cm.getColumnByField('freigabedatum').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'freigabe') - }); - cm.getColumnByField('antritt_bezeichnung').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'pruefungsantritt') - }); - cm.getColumnByField('sponsion').component.updateDefinition({ - title: this.$p.t('abschlusspruefung', 'sponsion') - }); - cm.getColumnByField('anmerkung').component.updateDefinition({ - title: this.$p.t('global', 'anmerkung') - }); - cm.getColumnByField('pruefungstyp_kurzbz').component.updateDefinition({ - title: this.$p.t('global', 'typ') - }); - cm.getColumnByField('abschlusspruefung_id').component.updateDefinition({ - title: this.$p.t('ui', 'abschlusspruefung_id') - }); - /* - cm.getColumnByField('actions').component.updateDefinition({ - title: this.$p.t('global', 'aktionen') - }); - */ - } - } - ], - tabulatorData: [], - lastSelected: null, - formData: { - typStg: null, - pruefungstyp_kurzbz: null, - akadgrad_id: null, - vorsitz: null, - pruefungsantritt_kurzbz: null, - abschlussbeurteilung_kurzbz: null, - datum: null, - sponsion: null, - pruefer1: null, - pruefer2: null, - pruefer3: null, - anmerkung: null, - protokoll: null, - note: null, - link: null - }, - statusNew: true, - arrTypen: [], - arrAntritte: [], - arrBeurteilungen: [], - arrAkadGrad: [], - arrNoten: [], - filteredMitarbeiter: [], - filteredPruefer: [], - abortController: { - mitarbeiter: null, - pruefer: null - }, - stgInfo: { typ: '', oe_kurzbz: '' } - } - }, - watch: { - student(){ - if (this.$refs.table) { - this.$refs.table.reloadTable(); - } - this.getStudiengangByKz(); - } - }, - methods: { - getStudiengangByKz(){ - this.stgInfo = { typ: '', oe_kurzbz: '' }; - this.$api - .call(ApiStudiengang.getStudiengangByKz(this.stg_kz)) - .then(result => this.stgInfo = result.data) - .catch(this.$fhcAlert.handleSystemError); - }, - actionNewAbschlusspruefung() { - this.resetForm(); - this.statusNew = true; - this.$refs.finalexamModal.show(); - this.setDefaultFormData(); - }, - actionEditAbschlusspruefung(abschlusspruefung_id) { - this.resetForm(); - this.statusNew = false; - this.$refs.finalexamModal.show(); - this.loadAbschlusspruefung(abschlusspruefung_id); - }, - actionDeleteAbschlusspruefung(abschlusspruefung_id) { - this.$fhcAlert - .confirmDelete() - .then(result => result - ? abschlusspruefung_id - : Promise.reject({handled: true})) - .then(this.deleteAbschlusspruefung) - .catch(this.$fhcAlert.handleSystemError); - }, - addNewAbschlusspruefung() { - const dataToSend = { - uid: this.student.uid, - formData: this.formData - }; - - return this.$refs.formFinalExam - .call(ApiStvAbschlusspruefung.addNewAbschlusspruefung(dataToSend)) - .then(response => { - this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); - this.hideModal('finalexamModal'); - this.resetForm(); - }) - .catch(this.$fhcAlert.handleSystemError) - .finally(() => { - this.reload(); - }); - }, - hideModal(modalRef){ - this.$refs[modalRef].hide(); - }, - reload() { - this.$refs.table.reloadTable(); - }, - loadAbschlusspruefung(abschlusspruefung_id) { - return this.$api - .call(ApiStvAbschlusspruefung.loadAbschlusspruefung(abschlusspruefung_id)) - .then(result => { - this.formData = result.data; - //TODO(Manu) check if cisRoot is okay - this.formData.link = this.cisRoot + 'index.ci.php/lehre/Pruefungsprotokoll/showProtokoll?abschlusspruefung_id=' + this.formData.abschlusspruefung_id + '&fhc_controller_id=67481e5ed5490'; - return result; - }) - .catch(this.$fhcAlert.handleSystemError); - }, - updateAbschlusspruefung(abschlusspruefung_id) { - const dataToSend = { - id: abschlusspruefung_id, - formData: this.formData - }; - return this.$refs.formFinalExam - .call(ApiStvAbschlusspruefung.updateAbschlusspruefung(dataToSend)) - .then(response => { - this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); - this.hideModal('finalexamModal'); - this.resetForm(); - }) - .catch(this.$fhcAlert.handleSystemError) - .finally(() => { - this.reload(); - }); - }, - deleteAbschlusspruefung(abschlusspruefung_id) { - return this.$api - .call(ApiStvAbschlusspruefung.deleteAbschlusspruefung(abschlusspruefung_id)) - .then(response => { - this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); - }) - .catch(this.$fhcAlert.handleSystemError) - .finally(() => { - this.reload(); - }); - }, - resetForm() { - this.formData.pruefungstyp_kurzbz = null; - this.formData.akadgrad_id = null; - this.formData.vorsitz = null; - this.formData.pruefungsantritt_kurzbz = null; - this.formData.abschlussbeurteilung_kurzbz = null; - this.formData.datum = null; //oder new Date(); - this.formData.sponsion = null; - this.formData.pruefer1 = null; - this.formData.pruefer2 = null; - this.formData.pruefer3 = null; - this.formData.anmerkung = null; - this.formData.protokoll = null; - this.formData.note = null; - this.formData.p1 = null; - this.formData.p2 = null; - this.formData.p3 = null; - this.formData.pv = null; - }, - search(event) { - if (this.abortController.mitarbeiter) { - this.abortController.mitarbeiter.abort(); - } - this.abortController.mitarbeiter = new AbortController(); - - return this.$api - .call(ApiStvAbschlusspruefung.getMitarbeiter(event.query)) - .then(result => { - this.filteredMitarbeiter = result.data.retval; - }); - }, - searchNotAkad(event) { - if (this.abortController.pruefer) { - this.abortController.pruefer.abort(); - } - this.abortController.pruefer = new AbortController(); - - return this.$api - .call(ApiStvAbschlusspruefung.getPruefer(event.query)) - .then(result => { - this.filteredPruefer = result.data.retval; - }); - }, - setDefaultFormData() { - - this.resetForm(); - - if (this.stgInfo.typ === 'b') { - this.formData.pruefungstyp_kurzbz = 'Bachelor'; - this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster'); - } - if (this.stgInfo.typ === 'd' || this.stgInfo === 'm') { - this.formData.pruefungstyp_kurzbz = 'Diplom'; - this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster'); - } - if (this.stgInfo.typ === 'lg') { - this.formData.pruefungstyp_kurzbz = 'lgabschluss'; - } - - if (!this.formData.akadgrad_id && this.arrAkadGrad.length > 0) { - this.formData.akadgrad_id = this.arrAkadGrad[0].akadgrad_id; - } - }, - printDocument(link) { - window.open(link, '_blank'); - }, - }, - created() { - this.$api - .call(ApiStvAbschlusspruefung.getTypenAbschlusspruefung()) - .then(result => { - this.arrTypen = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - this.$api - .call(ApiStvAbschlusspruefung.getTypenAntritte()) - .then(result => { - this.arrAntritte = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - this.$api - .call(ApiStvAbschlusspruefung.getBeurteilungen()) - .then(result => { - this.arrBeurteilungen = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - this.$api - .call(ApiStvAbschlusspruefung.getNoten()) - .then(result => { - this.arrNoten = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - this.$api - .call(ApiStvAbschlusspruefung.getAkadGrade(this.student.studiengang_kz)) - .then(result => { - this.arrAkadGrad = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - if (!this.student.length) { - this.$api - .call(ApiStudiengang.getStudiengangByKz(this.student.studiengang_kz)) - .then(result => { - this.stgInfo = result.data; - this.setDefaultFormData(); - }) - .catch(this.$fhcAlert.handleSystemError); - } else - this.getStudiengangByKz(); - }, - template: ` -
-

{{this.$p.t('stv','tab_finalexam')}}

- -
- -
- - - - - - - - - - - - - {{this.$p.t('global','details')}} -

[{{$p.t('ui', 'neu')}}]

-
- - - - - - - -
- -
- - - - -
- -
- - - - - -
- -
- - - - - - - -
- -
- - - - - -
- -
- - - - -
- -
- - - - -
- -
-
-

{{$p.t('abschlusspruefung', 'zurBeurteilung')}}

-
- -
- -
- - - -
- - - - -
-` -} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js index a6256ceb1..7f98948d4 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js @@ -1,4 +1,3 @@ -import {CoreFilterCmpt} from "../../../../filter/Filter.js"; import FormForm from '../../../../Form/Form.js'; import FormInput from '../../../../Form/Input.js'; import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"; @@ -7,7 +6,6 @@ import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js export default { components: { - CoreFilterCmpt, FormForm, FormInput, PvAutoComplete @@ -48,23 +46,12 @@ export default { } return lehreinheiten; - }, - //~ preparedFormData() { - //~ console.log("FOR DATA CALLEd"); - //~ if (this.formData.projektarbeit_id == null) delete(this.formData.projektarbeit_id); - //~ if (this.formData.firma) this.formData.firma_id = this.formData.firma.firma_id; - //~ delete(this.formData.firma); - //~ delete(this.formData.firma_name); - //~ delete(this.formData.lehrveranstaltung_id); - //~ // convert null value fields from string to null - //~ return this.formData; - //~ } + } }, props: { statusNew: Boolean, student: Object, - projektarbeit: Object, - stg_kz: Number + projektarbeit: Object }, data() { return { @@ -177,21 +164,13 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); }, - setFormData(arrTypen, arrLvs, arrNoten, projektarbeitData) { - this.arrTypen = arrTypen; - this.arrLvs = arrLvs; - this.arrNoten = arrNoten; - if (projektarbeitData) { - projektarbeitData.firma = {firma_id: projektarbeitData.firma_id, name: projektarbeitData.firma_name}; - this.formData = projektarbeitData; - } - }, loadProjektarbeit(projektarbeit_id) { 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) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js index 034b3fd6c..15c9aeeee 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -7,6 +7,7 @@ import PvAutoComplete from "../../../../../../../index.ci.php/public/js/componen import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js'; import ProjektarbeitDetails from "./Details.js"; +import Projektbetreuer from "./Projektbetreuer.js"; export default { components: { @@ -15,7 +16,8 @@ export default { FormForm, FormInput, PvAutoComplete, - ProjektarbeitDetails + ProjektarbeitDetails, + Projektbetreuer }, inject: { cisRoot: { @@ -174,7 +176,9 @@ export default { button.title = this.$p.t('ui', 'bearbeiten'); button.addEventListener('click', (event) => { let data = cell.getData(); - this.actionEditProjektarbeit(data.projektarbeit_id, data.studiensemester_kurzbz, data.lehrveranstaltung_id); + this.actionEditProjektarbeit( + data.projektarbeit_id, data.studiensemester_kurzbz, data.lehrveranstaltung_id, data.projekttyp_kurzbz + ); }); container.append(button); @@ -195,7 +199,6 @@ export default { }, ], layout: 'fitDataFill', - layoutColumnsOnNewData: false, height: 'auto', minHeight: '200', selectable: 1, @@ -240,25 +243,20 @@ export default { lehrveranstaltung_id: null } }, - //~ watch: { - //~ student(){ - //~ if (this.$refs.table) { - //~ this.$refs.table.reloadTable(); - //~ } - //~ this.getStudiengangByKz(); - //~ } - //~ }, methods: { actionNewProjektarbeit() { this.statusNew = true; this.$refs.projektarbeitDetails.resetForm(); this.$refs.projektarbeitDetails.getFormData(); + this.$refs.projektbetreuer.getData(); this.$refs.projektarbeitModal.show(); }, - actionEditProjektarbeit(projektarbeit_id, studiensemester_kurzbz, lehrveranstaltung_id) { + actionEditProjektarbeit(projektarbeit_id, studiensemester_kurzbz, lehrveranstaltung_id, projekttyp_kurzbz) { this.statusNew = false; this.$refs.projektarbeitDetails.getFormData(this.statusNew, studiensemester_kurzbz, lehrveranstaltung_id); + // TODO: maybe preload projektarbeit? not just on edit? this.$refs.projektarbeitDetails.loadProjektarbeit(projektarbeit_id); + this.$refs.projektbetreuer.getData(projektarbeit_id, studiensemester_kurzbz, projekttyp_kurzbz); this.$refs.projektarbeitModal.show(); }, actionDeleteProjektarbeit(projektarbeit_id) { @@ -271,41 +269,41 @@ export default { .catch(this.$fhcAlert.handleSystemError); }, addNewProjektarbeit() { - Promise.allSettled([ - this.$refs.projektarbeitDetails.addNewProjektarbeit() - ]).then((results) => { - let hasError = false; - results.forEach((promise_result) => { + this.$refs.projektbetreuer.validateProjektbetreuer() + .then(() => { + return this.$refs.projektarbeitDetails.addNewProjektarbeit(); + }) + .then((result) => { + const projektarbeit_id = result.data; + console.log(projektarbeit_id); - if (!(promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success")) { - - hasError = true; + if (!isNaN(projektarbeit_id)) { + return this.$refs.projektbetreuer.saveProjektbetreuer(projektarbeit_id); } - }); - - if (!hasError) { + }) + .then((result) => { + console.log(result); this.projektarbeitSaved(); - } - }); + }) + .catch(this.$fhcAlert.handleSystemError); }, updateProjektarbeit() { - Promise.allSettled( - [ - this.$refs.projektarbeitDetails.updateProjektarbeit() - ]).then((results) => { - let hasError = false; - results.forEach((promise_result) => { + this.$refs.projektbetreuer.validateProjektbetreuer() + .then(() => { + return this.$refs.projektarbeitDetails.updateProjektarbeit(); + }) + .then((result) => { + const projektarbeit_id = result.data; + console.log(projektarbeit_id); - if (!(promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success")) { - - hasError = true; + if (!isNaN(projektarbeit_id)) { + return this.$refs.projektbetreuer.saveProjektbetreuer(projektarbeit_id); } - }); - - if (!hasError) { + }) + .then((result) => { this.projektarbeitSaved(); - } - }); + }) + .catch(this.$fhcAlert.handleSystemError); }, deleteProjektarbeit(projektarbeit_id) { return this.$api @@ -325,7 +323,6 @@ export default { this.$refs.projektarbeitDetails.resetForm(); }, rowSelectionChanged(data) { - console.log("selection changed"); this.lastSelected = data.length > 0 ? data[0] : null; }, hideModal(modalRef){ @@ -362,7 +359,14 @@ export default {

{{$p.t('projektarbeit', 'projektarbeitBearbeiten')}}

- +
+
+ +
+
+ +
+
` -}; \ No newline at end of file +}; diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index a4381b00d..433a52d3e 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -41789,7 +41789,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Title', + 'text' => 'title', 'description' => '', 'insertvon' => 'system' ) @@ -41809,7 +41809,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Title English', + 'text' => 'title English', 'description' => '', 'insertvon' => 'system' ) @@ -41829,7 +41829,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Topic area', + 'text' => 'topic area', 'description' => '', 'insertvon' => 'system' ) @@ -41849,7 +41849,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Type', + 'text' => 'type', 'description' => '', 'insertvon' => 'system' ) @@ -41869,7 +41869,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Company', + 'text' => 'company', 'description' => '', 'insertvon' => 'system' ) @@ -41889,7 +41889,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Course', + 'text' => 'course', 'description' => '', 'insertvon' => 'system' ) @@ -41909,7 +41909,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Teaching unit', + 'text' => 'teaching unit', 'description' => '', 'insertvon' => 'system' ) @@ -41920,6 +41920,26 @@ and represent the current state of research on the topic. The prescribed citatio 'category' => 'projektarbeit', 'phrase' => 'betreuer', 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Betreuer', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'assessor', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'betreuerGross', + 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', @@ -41935,6 +41955,26 @@ and represent the current state of research on the topic. The prescribed citatio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'betreuerart', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Betreuerart', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'assessor type', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'projektarbeit', @@ -41949,7 +41989,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Grade', + 'text' => 'grade', 'description' => '', 'insertvon' => 'system' ) @@ -41969,7 +42009,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Hours', + 'text' => 'hours', 'description' => '', 'insertvon' => 'system' ) @@ -41989,7 +42029,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Hourly rate', + 'text' => 'hourly rate', 'description' => '', 'insertvon' => 'system' ) @@ -42029,7 +42069,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Start', + 'text' => 'start', 'description' => '', 'insertvon' => 'system' ) @@ -42049,7 +42089,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'End', + 'text' => 'end', 'description' => '', 'insertvon' => 'system' ) @@ -42089,7 +42129,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Locked until', + 'text' => 'locked until', 'description' => '', 'insertvon' => 'system' ) @@ -42109,7 +42149,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Annotation', + 'text' => 'annotation', 'description' => '', 'insertvon' => 'system' ) @@ -42129,7 +42169,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Company Id', + 'text' => 'company Id', 'description' => '', 'insertvon' => 'system' ) @@ -42215,6 +42255,326 @@ and represent the current state of research on the topic. The prescribed citatio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'neuePersonAnlegen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Neue Person anlegen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Create new person', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'titelPre', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Titel (Pre)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'title (Pre)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'titelPost', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Titel (Post)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'title (Post)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'weitereVornamen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Weitere Vornamen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'other first names', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'bestehendeAdresseUeberschreiben', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bestehende Adresse überschreiben', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Replace existing address', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'adresseHinzufuegen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Adresse hinzufügen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Add new address', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'adresseNichtAnlegen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Adresse nicht anlegen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Do not create address', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'land', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Land', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'nation', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'mobil', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Mobil', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'mobile phone', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'letzeAusbildung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Letzte Ausbildung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'most recent education', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'ausbildungsart', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ausbildungsart', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'education type', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'anmerkungen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anmerkungen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'notes', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'personAnlegen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Person anlegen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Create person', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'interessentAnlegen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'InteressentIn anlegen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Create candidate', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'personExistiertPruefung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Prüfung ob Person bereits existiert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Check if a person already exists', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'zurueck', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zurück', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Back', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), ); From 750b956dd22ce736fe8341312ec141dad0265cad Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 2 Jun 2025 15:28:10 +0200 Subject: [PATCH 022/312] Studentenverwaltung Projektarbeit: added button for editing contact data --- .../Details/Projektarbeit/Projektbetreuer.js | 31 +++++++++++++++++-- system/phrasesupdate.php | 20 ++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index 079b8b796..4f3095cab 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -1,18 +1,22 @@ 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 ApiStvProjektbetreuer from '../../../../../api/factory/stv/projektbetreuer.js'; export default { components: { CoreFilterCmpt, + BsModal, FormForm, FormInput, PvAutoComplete, - NewPerson + NewPerson, + Contact }, inject: { }, @@ -340,6 +344,10 @@ export default { this.$refs.newPersonModal.reset(); this.$refs.newPersonModal.open(); }, + actionKontaktdatenBearbeiten() { + if (!this.autocompleteSelectedBetreuer) return; + this.$refs.kontaktdatenModal.show(); + }, personSaved(result) { this.$api .call(ApiStvProjektbetreuer.getPerson(result.person_id)) @@ -384,9 +392,12 @@ export default {
-
+
+
+ +
@@ -455,6 +466,22 @@ export default { + + + + + + +
+
+ + +
+
+ +
` } diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 433a52d3e..d467252d7 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -42575,6 +42575,26 @@ and represent the current state of research on the topic. The prescribed citatio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'kontaktdatenBearbeiten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kontaktdaten bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Edit contact data', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ) ); From 3e3fa9c1eb286a960df67ce613e3ee82a02d0e12 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 3 Jun 2025 08:57:28 +0200 Subject: [PATCH 023/312] svnr aus dem fas entfernt --- .../api/frontend/v1/stv/Student.php | 1 - .../api/frontend/v1/stv/Students.php | 1 - cis/infoterminal/index.php | 1 - content/dokumentenakt.pdf.php | 3 - .../statistik/studentenexportextended.xls.php | 9 +-- content/student/studentDBDML.php | 2 - content/student/studentdetailoverlay.xul.php | 3 +- content/student/studentenoverlay.xul.php | 5 -- content/student/studentoverlay.js.php | 6 -- include/prestudent.class.php | 1 - include/student.class.php | 4 +- .../Studentenverwaltung/Details/Details.js | 10 --- .../Stv/Studentenverwaltung/List.js | 1 - public/js/infocenter/stammdaten.js | 1 - rdf/ausbildungsvertrag.xml.php | 6 -- rdf/student.rdf.php | 9 +-- rdf/studienblatt.xml.php | 4 - .../personen/import/interessentenimport.php | 3 +- vilesci/personen/personen_details.php | 33 +++++++-- vilesci/personen/preinteressent_anlegen.php | 73 +++++++------------ vilesci/personen/preinteressent_detail.php | 3 - vilesci/statistik/oehwaehlerverzeichnis.php | 1 - vilesci/statistik/studierendenliste_oeh.php | 1 - 23 files changed, 59 insertions(+), 122 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Student.php b/application/controllers/api/frontend/v1/stv/Student.php index f24ef62bb..817d5adbc 100644 --- a/application/controllers/api/frontend/v1/stv/Student.php +++ b/application/controllers/api/frontend/v1/stv/Student.php @@ -186,7 +186,6 @@ class Student extends FHCAPI_Controller 'gebdatum', 'gebort', 'geburtsnation', - 'svnr', 'ersatzkennzeichen', 'staatsbuergerschaft', 'matr_nr', diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index 5fb7b592c..c843eddd2 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -618,7 +618,6 @@ class Students extends FHCAPI_Controller $this->PrestudentModel->addSelect('wahlname'); $this->PrestudentModel->addSelect('vornamen'); $this->PrestudentModel->addSelect('titelpost'); - $this->PrestudentModel->addSelect('svnr'); $this->PrestudentModel->addSelect('ersatzkennzeichen'); $this->PrestudentModel->addSelect('gebdatum'); $this->PrestudentModel->addSelect('geschlecht'); diff --git a/cis/infoterminal/index.php b/cis/infoterminal/index.php index 1b4c7d637..04c591ee1 100644 --- a/cis/infoterminal/index.php +++ b/cis/infoterminal/index.php @@ -843,7 +843,6 @@ function meine_uid_informationen_detail($db,$uid,$count=0) $aktiv=$db->db_result($erg,0,"aktiv"); - $svnr=$db->db_result($erg,0,"svnr"); $titelpre=$db->db_result($erg,0,"titelpre"); $titelpost=$db->db_result($erg,0,"titelpost"); diff --git a/content/dokumentenakt.pdf.php b/content/dokumentenakt.pdf.php index a27b8d562..a737ee72d 100644 --- a/content/dokumentenakt.pdf.php +++ b/content/dokumentenakt.pdf.php @@ -307,8 +307,6 @@ foreach($prestudent_ids as $pid) $nation->load($prestudent->zgvnation); $zgvnation = $nation->kurztext; - $svnr = ($prestudent->svnr == '')?($prestudent->ersatzkennzeichen != ''?'Ersatzkennzeichen: '.$prestudent->ersatzkennzeichen:''):$prestudent->svnr; - foreach($adresse->result as $row_adresse) { if($row_adresse->heimatadresse) @@ -439,7 +437,6 @@ foreach($prestudent_ids as $pid) 'zustell_ort' => $zustellOrt, 'zustell_bundesland' => $zustellBundesland, 'geburtsnation' => $geburtsnation, - 'svnr' => $svnr, 'staatsbuergerschaft' => $staatsbuergerschaft, 'geschlecht' => $prestudent->geschlecht, 'telefonnummer' => $telefonnummer, diff --git a/content/statistik/studentenexportextended.xls.php b/content/statistik/studentenexportextended.xls.php index 89acbe18f..5ab34d026 100644 --- a/content/statistik/studentenexportextended.xls.php +++ b/content/statistik/studentenexportextended.xls.php @@ -125,8 +125,6 @@ $worksheet->write($zeile, ++$i, "PERSONENKENNZEICHEN", $format_bold); $maxlength[$i] = 19; $worksheet->write($zeile, ++$i, "STAATSBÜRGERSCHAFT", $format_bold); $maxlength[$i] = 16; -$worksheet->write($zeile, ++$i, "SVNR", $format_bold); -$maxlength[$i] = 4; $worksheet->write($zeile, ++$i, "PERSON_ID", $format_bold); $maxlength[$i] = 6; $worksheet->write($zeile, ++$i, "ERSATZKENNZEICHEN", $format_bold); @@ -396,12 +394,7 @@ function draw_content($row) $worksheet->write($zeile, $i, $row->staatsbuergerschaft); $i++; - //SVNR - if (mb_strlen($row->svnr) > $maxlength[$i]) - $maxlength[$i] = mb_strlen($row->svnr); - $worksheet->write($zeile, $i, $row->svnr); - $i++; - + //Person_id if (mb_strlen($row->person_id) > $maxlength[$i]) $maxlength[$i] = mb_strlen($row->person_id); diff --git a/content/student/studentDBDML.php b/content/student/studentDBDML.php index e1bcffd84..041af9c20 100644 --- a/content/student/studentDBDML.php +++ b/content/student/studentDBDML.php @@ -594,7 +594,6 @@ if(!$error) $student->anmerkungen = $_POST['anmerkung']; $student->homepage = $_POST['homepage']; $student->matr_nr = $_POST['matr_nr']; - $student->svnr = $_POST['svnr']; $student->ersatzkennzeichen = $_POST['ersatzkennzeichen']; $student->familienstand = $_POST['familienstand']; $student->geschlecht = $_POST['geschlecht']; @@ -746,7 +745,6 @@ if(!$error) $person->gebzeit = $_POST['geburtszeit']; $person->anmerkungen = $_POST['anmerkung']; $person->homepage = $_POST['homepage']; - $person->svnr = $_POST['svnr']; $person->ersatzkennzeichen = $_POST['ersatzkennzeichen']; $person->familienstand = $_POST['familienstand']; $person->geschlecht = $_POST['geschlecht']; diff --git a/content/student/studentdetailoverlay.xul.php b/content/student/studentdetailoverlay.xul.php index 132667395..36740d209 100644 --- a/content/student/studentdetailoverlay.xul.php +++ b/content/student/studentdetailoverlay.xul.php @@ -134,8 +134,7 @@ echo ''; -
- - bezeichnung : $studiengangbezeichnung; $studiengang_bezeichnung_englisch = empty($studiengangbezeichnung_englisch) ? $studiengang->english : $studiengangbezeichnung_englisch; - $svnr = ($student->svnr == '')?'Ersatzkennzeichen: '.$student->ersatzkennzeichen:$student->svnr; - //Wenn Lehrgang, dann Erhalter-KZ vor die Studiengangs-Kz hängen if ($studiengang->studiengang_kz<0) { @@ -146,7 +144,6 @@ foreach($uid_arr as $uid) echo "\t\t".$gebdatum."\n"; echo "\t\t".$student->gebort."\n"; echo "\t\t".$staatsbuergerschaft->langtext."\n"; - echo "\t\t".$svnr."\n"; echo "\t\t".trim($student->matrikelnr)."\n"; echo "\t\t".$db->convert_html_chars($studiengang_bezeichnung)."\n"; echo "\t\t".$db->convert_html_chars($studiengang_bezeichnung_englisch)."\n"; @@ -340,8 +337,6 @@ foreach($prestudent_arr as $prest_id) $studiengang_bezeichnung = empty($studiengangbezeichnung) ? $studiengang->bezeichnung : $studiengangbezeichnung; $studiengang_bezeichnung_englisch = empty($studiengangbezeichnung_englisch) ? $studiengang->english : $studiengangbezeichnung_englisch; - $svnr = ($person->svnr == '')?($person->ersatzkennzeichen != ''?'Ersatzkennzeichen: '.$person->ersatzkennzeichen:''):$person->svnr; - //Wenn Lehrgang, dann Erhalter-KZ vor die Studiengangs-Kz hängen if ($studiengang->studiengang_kz<0) { @@ -363,7 +358,6 @@ foreach($prestudent_arr as $prest_id) echo "\t\t".$gebdatum."\n"; echo "\t\t".$person->gebort."\n"; echo "\t\t".$staatsbuergerschaft->langtext."\n"; - echo "\t\t".$svnr."\n"; echo "\t\t".$db->convert_html_chars($studiengang_bezeichnung)."\n"; echo "\t\t".$db->convert_html_chars($studiengang_bezeichnung_englisch)."\n"; echo "\t\t".$studiengang->kurzbzlang."\n"; diff --git a/rdf/student.rdf.php b/rdf/student.rdf.php index 61c903bd6..439ded6ec 100644 --- a/rdf/student.rdf.php +++ b/rdf/student.rdf.php @@ -235,7 +235,6 @@ function draw_content_liste($row) vorname.']]> nachname.']]> geschlecht.']]> - '.($row->svnr==''?' ':'svnr.']]>').' '.($row->ersatzkennzeichen==''?' ':'ersatzkennzeichen.']]>').' convertISODate($row->gebdatum).']]> gebdatum.']]> @@ -349,7 +348,6 @@ function draw_content($row) gebzeit.']]> '.($row->anmerkungen==''?' ':'anmerkungen.']]>').' anrede.']]> - svnr.']]> ersatzkennzeichen.']]> familienstand.']]> geschlecht.']]> @@ -470,7 +468,6 @@ function draw_empty_content() - @@ -647,7 +644,7 @@ if($xmlformat=='rdf') $sql_query=" SELECT p.person_id, tbl_student.prestudent_id, tbl_benutzer.uid, titelpre, titelpost,vorname, wahlname, vornamen, geschlecht, - nachname, gebdatum, tbl_prestudent.anmerkung,ersatzkennzeichen,svnr, tbl_student.matrikelnr, p.anmerkung as anmerkungen, + nachname, gebdatum, tbl_prestudent.anmerkung,ersatzkennzeichen, tbl_student.matrikelnr, p.anmerkung as anmerkungen, tbl_studentlehrverband.semester, tbl_studentlehrverband.verband, tbl_studentlehrverband.gruppe, tbl_student.studiengang_kz, aufmerksamdurch_kurzbz, mentor, public.tbl_benutzer.aktiv AS bnaktiv, ( SELECT kontakt @@ -985,8 +982,7 @@ if($xmlformat=='rdf') $qry .= " prestudent_id = ".$db->db_add_param($searchItems_string_orig).";"; else $qry .= " matrikelnr = ".$db->db_add_param($searchItems_string_orig)." OR - matr_nr = ".$db->db_add_param($searchItems_string_orig)." OR - svnr = ".$db->db_add_param($searchItems_string_orig).";"; + matr_nr = ".$db->db_add_param($searchItems_string_orig).";"; } if($result = $db->db_query($qry)) { @@ -1282,7 +1278,6 @@ else anrede.']]> geschlecht.']]> - svnr.']]> ersatzkennzeichen.']]> familienstand.']]> diff --git a/rdf/studienblatt.xml.php b/rdf/studienblatt.xml.php index 3ee805c04..a9b78d968 100644 --- a/rdf/studienblatt.xml.php +++ b/rdf/studienblatt.xml.php @@ -105,9 +105,6 @@ foreach($uid_arr as $uid) $staatsbuergerschaft = new nation(); $staatsbuergerschaft->load($student->staatsbuergerschaft); - - $svnr = ($student->svnr == '')?'Ersatzkennzeichen: '.$student->ersatzkennzeichen:$student->svnr; - $geschlecht_obj = new geschlecht(); $geschlecht_obj->load($student->geschlecht); @@ -143,7 +140,6 @@ foreach($uid_arr as $uid) echo "\t\t\n"; echo "\t\tgebort."]]>\n"; echo "\t\tlangtext."]]>\n"; - echo "\t\t\n"; echo "\t\tmatr_nr)."]]>\n"; echo "\t\tmatrikelnr)."]]>\n"; echo "\t\tstudiengangbezeichnung."]]>\n"; diff --git a/vilesci/personen/import/interessentenimport.php b/vilesci/personen/import/interessentenimport.php index d1830eaeb..8734a1353 100644 --- a/vilesci/personen/import/interessentenimport.php +++ b/vilesci/personen/import/interessentenimport.php @@ -1167,8 +1167,7 @@ if ($where != '') } } $status = mb_substr($status, 0, mb_strlen($status)-2); - - echo ''."$row->nachname$row->vorname$row->wahlname$row->vornamen$row->gebdatum$row->svnr".($row->geschlecht=='m'?'männlich':'weiblich').""; + echo ''."$row->nachname$row->vorname$row->wahlname$row->vornamen$row->gebdatum".((strpos($status, 'Mitarbeiter') !== false) ? $row->svnr : '')."".($row->geschlecht=='m'?'männlich':'weiblich').""; $qry_adr = "SELECT * FROM public.tbl_adresse WHERE person_id=".$db->db_add_param($row->person_id, FHC_INTEGER); if ($result_adr = $db->db_query($qry_adr)) while ($row_adr = $db->db_fetch_object($result_adr)) diff --git a/vilesci/personen/personen_details.php b/vilesci/personen/personen_details.php index d1dcd8815..b274558bd 100644 --- a/vilesci/personen/personen_details.php +++ b/vilesci/personen/personen_details.php @@ -125,6 +125,7 @@ $verband = (isset($_POST['verband'])?$_POST['verband']:''); $gruppe = (isset($_POST['gruppe'])?$_POST['gruppe']:''); $dms_id_lichtbild = ''; +$is_mitarbeiter = false; if($uid!='') { $qry = "SELECT person_id, true as mitarbeiter FROM campus.vw_mitarbeiter WHERE uid=".$db->db_add_param($uid)." @@ -144,7 +145,17 @@ if($uid!='') else die('Fehler beim Ermitteln der UID'); } - +else if ($person_id !='') +{ + $qry = "SELECT person_id, true as mitarbeiter FROM campus.vw_mitarbeiter WHERE person_id=".$db->db_add_param($person_id); + if($result = $db->db_query($qry)) + { + if($row = $db->db_fetch_object($result)) + { + $is_mitarbeiter = ($row->mitarbeiter=='t'?true:false); + } + } +} if(isset($_POST['saveperson'])) { if(!$rechte->isBerechtigt('student/stammdaten', null, 'su') && !$rechte->isBerechtigt('mitarbeiter/stammdaten', null, 'su')) @@ -163,7 +174,8 @@ if(isset($_POST['saveperson'])) $person->gebdatum = $geburtsdatum; $person->gebort = $geburtsort; $person->geburtsnation = $geburtsnation; - $person->svnr = $svnr; + if ($is_mitarbeiter) + $person->svnr = $svnr; $person->ersatzkennzeichen = $ersatzkennzeichen; $person->gebzeit = $geburtszeit; $person->staatsbuergerschaft = $staatsbuergerschaft; @@ -332,7 +344,8 @@ if(!$error_person_save) $geburtsdatum = $person->gebdatum; $geburtsort = $person->gebort; $geburtsnation = $person->geburtsnation; - $svnr = $person->svnr; + if ($is_mitarbeiter) + $svnr = $person->svnr; $ersatzkennzeichen = $person->ersatzkennzeichen; $geburtszeit = $person->gebzeit; $staatsbuergerschaft = $person->staatsbuergerschaft; @@ -398,10 +411,16 @@ foreach ($nation->nation as $row_nation) echo " - - SVNR - - Ersatzkennzeichen +"; + +if ($is_mitarbeiter) +{ + echo "SVNR + "; +} + +echo + "Ersatzkennzeichen Geburtszeit diff --git a/vilesci/personen/preinteressent_anlegen.php b/vilesci/personen/preinteressent_anlegen.php index 3ba5865d2..cc9ecbf53 100644 --- a/vilesci/personen/preinteressent_anlegen.php +++ b/vilesci/personen/preinteressent_anlegen.php @@ -140,7 +140,6 @@ function disablefields(obj) document.getElementById('vorname').disabled=val; document.getElementById('geschlecht').disabled=val; document.getElementById('geburtsdatum').disabled=val; - document.getElementById('svnr').disabled=val; document.getElementById('ersatzkennzeichen').disabled=val; //document.getElementById('adresse').disabled=val; //document.getElementById('plz').disabled=val; @@ -159,21 +158,6 @@ function disablefields(obj) } } -function GeburtsdatumEintragen() -{ - svnr = document.getElementById('svnr').value; - gebdat = document.getElementById('geburtsdatum'); - - if(svnr.length==10 && gebdat.value=='') - { - var tag = svnr.substr(4,2); - var monat = svnr.substr(6,2); - var jahr = svnr.substr(8,2); - - gebdat.value='19'+jahr+'-'+monat+'-'+tag; - } -} - function disablefields2(val) { document.getElementById('adresse').disabled=val; @@ -368,7 +352,6 @@ $email = (isset($_REQUEST['email'])?$_REQUEST['email']:''); $telefon = (isset($_REQUEST['telefon'])?$_REQUEST['telefon']:''); $mobil = (isset($_REQUEST['mobil'])?$_REQUEST['mobil']:''); $person_id = (isset($_REQUEST['person_id'])?$_REQUEST['person_id']:''); -$svnr = (isset($_REQUEST['svnr'])?$_REQUEST['svnr']:''); $ersatzkennzeichen = (isset($_REQUEST['ersatzkennzeichen'])?$_REQUEST['ersatzkennzeichen']:''); $ueberschreiben = (isset($_REQUEST['ueberschreiben'])?$_REQUEST['ueberschreiben']:''); @@ -413,7 +396,6 @@ if(isset($_POST['save'])) $person->geschlecht = $geschlecht; $person->gebdatum = $datum_obj->formatDatum($geburtsdatum,'Y-m-d'); $person->staatsbuergerschaft = $nation; - $person->svnr = $svnr; $person->ersatzkennzeichen = $ersatzkennzeichen; $person->aktiv = true; $person->insertamum = date('Y-m-d H:i:s'); @@ -689,39 +671,38 @@ if($result = $db->db_query($qry)) } echo ''; echo ''; -echo 'SVNR'; echo 'Ersatzkennzeichen'; echo 'Geburtsdatum (Format dd.mm.JJJJ)'; echo 'Geburtsort'; -echo 'Geburtsnation'; +echo 'Geburtsnation'; echo ''; -echo 'Sprache'; $sprache_obj = new sprache(); $sprache_obj->getAll(); foreach($sprache_obj->result as $row) -{ - if($row->sprache==$sprache) - $selected='selected'; - else - $selected=''; - - echo ""; -} -echo ''; +{ + if($row->sprache==$sprache) + $selected='selected'; + else + $selected=''; + + echo ""; +} +echo ''; echo ''; echo '
Adresse'; echo ' - + diff --git a/cis/private/lehre/pruefung/pruefungsanmeldung.json.php b/cis/private/lehre/pruefung/pruefungsanmeldung.json.php index 54c064d14..c286f666e 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldung.json.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldung.json.php @@ -106,7 +106,7 @@ switch($method) case 'getStudiengaenge': $data = getStudiengaenge(); break; - case 'getPruefungenStudiengang': + case 'getPruefungenStudiensemester': $studiensemester = filter_input(INPUT_POST,"studiensemester"); $data = getPruefungenStudiengangBySemester($studiensemester); break; @@ -966,9 +966,13 @@ function alleBestaetigen($uid) global $p; $lehrveranstaltung_id = $_REQUEST["lehrveranstaltung_id"]; $pruefungstermin_id = $_REQUEST["termin_id"]; + $emails = $_REQUEST["emails"]; $pruefungstermin = new pruefungstermin($pruefungstermin_id); $pruefungsanmeldung = new pruefungsanmeldung(); $pranmeldungen = $pruefungsanmeldung->getAnmeldungenByTermin($pruefungstermin_id, $lehrveranstaltung_id); + + $mail_benutzer = []; + $mail_inhalt = []; foreach($pranmeldungen as $a) { $anmeldung = new pruefungsanmeldung($a->pruefungsanmeldung_id); @@ -982,6 +986,13 @@ function alleBestaetigen($uid) $ma = new mitarbeiter($uid); $datum = new datum(); $ort = new ort($termin->ort_kurzbz); + + $ortbezeichnung = $ort->bezeichnung; + if (is_null($termin->ort_kurzbz) && !is_null($termin->anderer_raum)) + { + $ortbezeichnung = $termin->anderer_raum; + } + $pruefung = new pruefungCis($termin->pruefung_id); $to = $anm->uid."@".DOMAIN; @@ -1001,17 +1012,66 @@ function alleBestaetigen($uid) } else $html .= $p->t('pruefung/emailBodyTermin')." ".$datum->formatDatum($termin->von, "d.m.Y")." ".$p->t('pruefung/emailBodyUm')." ".$datum->formatDatum($termin->von, "H:i")."
"; - $html .= $p->t('pruefung/anmeldungErfolgreich')." ".$ort->bezeichnung."
"; + $html .= $p->t('pruefung/anmeldungErfolgreich')." ".$ortbezeichnung."
"; $html .= "
"; $html .= "".$p->t('pruefung/emailBodyLinkZurAnmeldung')."
"; $html .= "
"; + $mail_benutzer[] = [ + 'uid' => $anm->uid + ]; + + if (empty($mail_inhalt)) + { + $mail_inhalt = array( + 'von' => $ma->vorname." ".$ma->nachname, + 'lv' => $lv->bezeichnung, + 'ort' => $ortbezeichnung, + 'datum' => $datum->formatDatum($termin->von, "d.m.Y") . ' ' . $p->t('pruefung/emailBodyUm') . ' ' . (isset($von) ? $von : $datum->formatDatum($termin->von, "H:i")), + 'dauer' => $pruefung->einzeln ? ($pruefung->pruefungsintervall . ' ' . $p->t('pruefung/emailBodyMinuten')): ''); + } + $mail = new mail($to, $from, $subject,$p->t('pruefung/emailBodyBitteHtmlSicht')); $mail->setHTMLContent($html); $mail->send(); } } } + + if (!empty($emails) && !empty($mail_inhalt)) + { + foreach ($emails as $email) + { + $from = "noreply@".DOMAIN; + $subject = $p->t('pruefung/emailSubjectAnmeldungBestaetigung'); + $html = $p->t('pruefung/sammelemailBody',array($mail_inhalt['lv'], $mail_inhalt['datum'], $mail_inhalt['von'])); + + if ($mail_inhalt['ort']) + { + $html .= $p->t('pruefung/sammelemailBody2',array($mail_inhalt['ort'])); + } + + $html .= "
Nation'; + echo '
NachnameVornameGebDatumSVNRGeschlechtAdresseStatusDetails
'; while($row = $db->db_fetch_object($result)) { $status = ''; @@ -905,7 +886,7 @@ if($where!='') } $status = mb_substr($status, 0, mb_strlen($status)-2); - echo ''; echo ''; -//SVNR -echo ""; //Ersatzkennzeichen echo ""; //Geschlecht diff --git a/vilesci/statistik/oehwaehlerverzeichnis.php b/vilesci/statistik/oehwaehlerverzeichnis.php index f7ea9447e..d94301f99 100644 --- a/vilesci/statistik/oehwaehlerverzeichnis.php +++ b/vilesci/statistik/oehwaehlerverzeichnis.php @@ -61,7 +61,6 @@ header( 'Content-Disposition: attachment;filename='.$filename); $qry=" SELECT * FROM ( SELECT DISTINCT ON (matrikelnr) matrikelnr AS personenkennzeichen, - tbl_person.svnr, tbl_person.ersatzkennzeichen, tbl_person.gebdatum, tbl_person.nachname, diff --git a/vilesci/statistik/studierendenliste_oeh.php b/vilesci/statistik/studierendenliste_oeh.php index 3480e76cd..a99cbde54 100644 --- a/vilesci/statistik/studierendenliste_oeh.php +++ b/vilesci/statistik/studierendenliste_oeh.php @@ -60,7 +60,6 @@ header( 'Content-Disposition: attachment;filename='.$filename); $qry=" SELECT * FROM ( SELECT DISTINCT ON (matrikelnr) matrikelnr AS personenkennzeichen, - tbl_person.svnr, tbl_person.ersatzkennzeichen, tbl_person.gebdatum, tbl_person.nachname, From fe81e7fb7c5bb528f911d315bd64c28c894fb0e6 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Tue, 3 Jun 2025 21:18:03 +0200 Subject: [PATCH 024/312] Studierendenverwaltung Projektarbeit: added button for Projektarbeit download --- .../api/frontend/v1/stv/Projektbetreuer.php | 26 +- .../Details/Projektarbeit/Projektbetreuer.js | 251 ++++++++++-------- system/phrasesupdate.php | 20 ++ 3 files changed, 171 insertions(+), 126 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index 34f626eca..7a1b803b9 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -3,6 +3,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); use \DateTime as DateTime; +use CI3_Events as Events; class Projektbetreuer extends FHCAPI_Controller { @@ -85,6 +86,21 @@ class Projektbetreuer extends FHCAPI_Controller //~ } //~ } + 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->projektarbeitDownload = $downloadLink; + } + $this->terminateWithSuccess($this->_addFullNameToBetreuer($projektbetreuer)); } @@ -105,11 +121,7 @@ class Projektbetreuer extends FHCAPI_Controller foreach ($projektbetreuer as $pb) { - if ($this->_validate($pb) == false) - { - $this->addMeta('test', 'foisch'); - $this->terminateWithValidationErrors($this->form_validation->error_array()); - } + if ($this->_validate($pb) == false) $this->terminateWithValidationErrors($this->form_validation->error_array()); } $result = null; @@ -210,10 +222,6 @@ class Projektbetreuer extends FHCAPI_Controller $result = $this->StundensatzModel->getStundensatzForMitarbeiter($person_id, $studiensemester_kurzbz); - $this->addMeta('res', $result); - - //if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - return $this->terminateWithSuccess($result); } diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index 4f3095cab..bf459ebc5 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -113,6 +113,7 @@ export default { arrNoten: [], filteredBetreuer: [], autocompleteSelectedBetreuer: null, + projektarbeitDownload: null, abortController: { betreuer: null } @@ -146,6 +147,7 @@ export default { if (idx >= 0) { betreuer = projektbetreuerListe[idx]; this.formData = betreuer; + if (betreuer.projektarbeitDownload) this.projektarbeitDownload = betreuer.projektarbeitDownload this.autocompleteSelectedBetreuer = { person_id: this.formData.person_id, name: this.formData.name, @@ -268,6 +270,7 @@ export default { }, resetForm() { this.formData = this.getDefaultFormData(); + this.projektarbeitDownload = null; this.autocompleteSelectedBetreuer = null; this.initialFormData = null; if (this.projekttyp_kurzbz) this.setDefaultStunden(this.projekttyp_kurzbz); @@ -358,130 +361,144 @@ export default { } }, template: ` -
+
- {{this.$p.t('projektarbeit','betreuerGross')}} - +
- - + {{this.$p.t('projektarbeit','betreuerGross')}} + - -
- - -
+ + -
-
- -
-
- -
-
- -
- -
+ + +
+ + + + + + + + + +
+
+ + +
+
+ +
` } diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index d467252d7..d10f915cc 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -42594,6 +42594,26 @@ and represent the current state of research on the topic. The prescribed citatio 'insertvon' => 'system' ) ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'projektbeurteilungErstellen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Projektbeurteilung erstellen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Create project assessment document', + 'description' => '', + 'insertvon' => 'system' + ) + ) ) ); From e26bce2cf7d21d78e59c09ba16c46097c6d4ef0b Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Tue, 10 Jun 2025 14:14:32 +0200 Subject: [PATCH 025/312] removed merge text from code --- application/models/ressource/Stundensatz_model.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/models/ressource/Stundensatz_model.php b/application/models/ressource/Stundensatz_model.php index 553bdd20b..c8ee367d8 100644 --- a/application/models/ressource/Stundensatz_model.php +++ b/application/models/ressource/Stundensatz_model.php @@ -150,4 +150,3 @@ class Stundensatz_model extends DB_Model return $default_stundensatz; } } -==== BASE ==== From fce55e38aeb132123a2758367ac4e05f99b7a6d3 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Thu, 12 Jun 2025 17:18:52 +0200 Subject: [PATCH 026/312] more & better anw phrasen --- system/phrasesupdate.php | 114 +++++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 9 deletions(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 9a461650b..ad0f7e347 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -30087,18 +30087,58 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'entschuldigungAutoEmailBetreff', + 'phrase' => 'entFullEmailBetreff', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Entschuldigung zur Befreiung der Anwesenheitspflicht: Neues Dokument wurde hochgeladen.', + 'text' => 'Neue Entschuldigung zur Befreiung der Anwesenheitspflicht: Neues Dokument wurde hochgeladen.', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'Excuse note for digital attendances - a new document has been uploaded.', + 'text' => 'New Excuse note for digital attendances - a new document has been uploaded.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'entNewEmailBetreff', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Neue Entschuldigung zur Befreiung der Anwesenheitspflicht: Dokument wird nachgereicht.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'New Excuse note for digital attendances - Document will be submitted later', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'entEditEmailBetreff', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Entschuldigung zur Befreiung der Anwesenheitspflicht: Dokument wurde eingereicht.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Excuse note for digital attendances - Document has been submitted', 'description' => '', 'insertvon' => 'system' ) @@ -32004,17 +32044,13 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Im Entschuldigungsmanagement können Sie als Studiengangsassistenz beziehungsweise als Administrator die von Studenten hochgeladenen Entschuldigungsdokumente überprüfen und den Status entsprechend vergeben. - - Bitte beachten Sie dass nur Entschuldigungen INNERHALB des angegebenen Zeitraumes angezeigt werden. Sollten Sie nach einer lang wirken Entschuldigung suchen, müssen Sie die Zeitspanne entsprechend weit setzen.', + 'text' => 'Im Entschuldigungsmanagement können Sie als Studiengangsassistenz beziehungsweise als Administrator die von Studenten hochgeladenen Entschuldigungsdokumente überprüfen und den Status entsprechend vergeben.', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'In the excuse management, you as a course assistant or administrator can check the excuse documents uploaded by students and assign the status accordingly. - - Please note that only excuses WITHIN the specified time period are displayed. If you are looking for a long-lasting excuse, you must set the time period accordingly.', + 'text' => 'In the excuse management, you as a course assistant or administrator can check the excuse documents uploaded by students and assign the status accordingly.', 'description' => '', 'insertvon' => 'system' ) @@ -32500,6 +32536,66 @@ array( ) ) ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'anwUserEntry', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anwesenheitseintrag', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Attendance Entry', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'highlightsettings', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Datumsmarkierung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Date Highlighting', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'editAnwKontrolle', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anwesenheitskontrolle bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Edit Attendance Checks', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // // DIGITALE ANWESENHEITEN PHRASEN END // From d15d27b3e14dbbf179fa85e4167b71c09c49c5f0 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Fri, 13 Jun 2025 16:19:53 +0200 Subject: [PATCH 027/312] =?UTF-8?q?Studierendenverwaltung=20Projektarbeit:?= =?UTF-8?q?=20added=20Vertr=C3=A4ge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/frontend/v1/stv/Config.php | 6 +- .../api/frontend/v1/stv/Projektbetreuer.php | 2 +- .../api/frontend/v1/stv/Vertrag.php | 104 +++++++++++ .../models/accounting/Vertrag_model.php | 20 +++ public/js/api/factory/stv/vertrag.js | 20 +++ .../Details/Projektarbeit/Details.js | 6 +- .../Details/Projektarbeit/Projektarbeit.js | 24 --- .../Details/Projektarbeit/Projektbetreuer.js | 103 +++++++---- .../Details/Projektarbeit/Vertrag.js | 168 ++++++++++++++++++ system/phrasesupdate.php | 68 ++++++- 10 files changed, 457 insertions(+), 64 deletions(-) create mode 100644 application/controllers/api/frontend/v1/stv/Vertrag.php create mode 100644 public/js/api/factory/stv/vertrag.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 98a06ffb5..83173375e 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -127,7 +127,11 @@ class Config extends FHCAPI_Controller $result['projektarbeit'] = [ 'title' => $this->p->t('stv', 'tab_projektarbeit'), 'component' => './Stv/Studentenverwaltung/Details/Projektarbeit.js', - 'config' => $config['projektarbeit'] + 'config' => array_merge( + $config['projektarbeit'], + ['showVertragsdetails' => + defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN') && FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN] + ) ]; $result['mobility'] = [ diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index 7a1b803b9..af0a11d0f 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -98,7 +98,7 @@ class Projektbetreuer extends FHCAPI_Controller $downloadLink = $value; } ); - $pb->projektarbeitDownload = $downloadLink; + $pb->beurteilungDownloadLink = $downloadLink; } $this->terminateWithSuccess($this->_addFullNameToBetreuer($projektbetreuer)); diff --git a/application/controllers/api/frontend/v1/stv/Vertrag.php b/application/controllers/api/frontend/v1/stv/Vertrag.php new file mode 100644 index 000000000..f94fe795e --- /dev/null +++ b/application/controllers/api/frontend/v1/stv/Vertrag.php @@ -0,0 +1,104 @@ + ['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); + } +} diff --git a/application/models/accounting/Vertrag_model.php b/application/models/accounting/Vertrag_model.php index c17c676c7..4c036369b 100644 --- a/application/models/accounting/Vertrag_model.php +++ b/application/models/accounting/Vertrag_model.php @@ -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); diff --git a/public/js/api/factory/stv/vertrag.js b/public/js/api/factory/stv/vertrag.js new file mode 100644 index 000000000..9070936da --- /dev/null +++ b/public/js/api/factory/stv/vertrag.js @@ -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 + }; + } +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js index 4f833621d..c2269c1b3 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js @@ -15,15 +15,13 @@ export default { defaultSemester: { from: 'defaultSemester' } - //~ cisRoot: { - //~ from: 'cisRoot' - //~ }, //~ config: { //~ from: 'config', //~ required: true //~ } }, computed: { + // prepared Lehreinheiten (with compound Bezeichnung) arrLes() { let lehreinheiten = []; if (this.formData.lehrveranstaltung_id) { @@ -133,7 +131,6 @@ export default { //~ //this.$fhcAlert.handleSystemError(promise_result); //~ } //~ //let data = promise_result.value.data; - //~ console.log(promise_result.status); //~ }); //~ if (!hasError) { @@ -214,6 +211,7 @@ export default { lvChanged(event) { this.formData.lehreinheit_id = null; }, + // enrich and modify data before sending getPreparedFormData() { let preparedFormData = JSON.parse(JSON.stringify(this.formData)); // deep copy diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js index 8314db8b3..3f9958a26 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -30,29 +30,6 @@ export default { from: '$reloadList', required: true } - //~ isBerechtigtDocAndOdt: { - //~ from: 'hasPermissionOutputformat', - //~ default: false - //~ } - }, - computed: { - //~ studentUids() { - //~ if (this.student.uid) - //~ { - //~ return [this.student.uid]; - //~ } - //~ return this.student.map(e => e.uid); - //~ }, - //~ studentKzs(){ - //~ if (this.student.uid) - //~ { - //~ return [this.student.studiengang_kz]; - //~ } - //~ return this.student.map(e => e.studiengang_kz); - //~ }, - //~ stg_kz(){ - //~ return this.studentKzs.length > 0 ? this.studentKzs.length[0] : null; - //~ } }, props: { student: Object @@ -271,7 +248,6 @@ export default { actionEditProjektarbeit(projektarbeit_id, studiensemester_kurzbz, lehrveranstaltung_id, projekttyp_kurzbz) { this.statusNew = false; this.$refs.projektarbeitDetails.getFormData(this.statusNew, studiensemester_kurzbz, lehrveranstaltung_id); - // TODO: maybe preload projektarbeit? not just on edit? this.$refs.projektarbeitDetails.loadProjektarbeit(projektarbeit_id); this.$refs.projektbetreuer.getData(projektarbeit_id, studiensemester_kurzbz, projekttyp_kurzbz); this.$refs.projektarbeitModal.show(); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index bf459ebc5..8368534c7 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -5,6 +5,7 @@ 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'; @@ -16,9 +17,13 @@ export default { FormInput, PvAutoComplete, NewPerson, - Contact + Contact, + Vertrag }, - inject: { + provide() { + return { + configShowVertragsdetails: this.config.showVertragsdetails + } }, computed: { betreuerFormOpened() { @@ -107,13 +112,13 @@ export default { initialFormData: null, defaultFormDataValues: {stunden: null, stundensatz: null}, projektarbeit_id: null, - statusNew: true, editedBetreuerIdx: -1, arrBetreuerart: [], arrNoten: [], filteredBetreuer: [], autocompleteSelectedBetreuer: null, - projektarbeitDownload: null, + beurteilungDownloadLink: null, + vertragFieldsDisabled: false, abortController: { betreuer: null } @@ -122,19 +127,19 @@ export default { methods: { actionNewProjektbetreuer() { this.resetForm(); - this.statusNew = true; this.newMode = !this.newMode; this.editMode = false; this.captureFormData(); }, actionEditProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) { - - this.statusNew = false; 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 => @@ -144,18 +149,29 @@ export default { ); let betreuer = []; - if (idx >= 0) { + if (idx >= 0) { // if betreuer found betreuer = projektbetreuerListe[idx]; + + // set currently edited betreuera this.formData = betreuer; - if (betreuer.projektarbeitDownload) this.projektarbeitDownload = betreuer.projektarbeitDownload + + // set download link + if (betreuer.beurteilungDownloadLink) this.beurteilungDownloadLink = betreuer.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 + 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); @@ -167,10 +183,12 @@ export default { ? {projektarbeit_id, person_id, betreuerart_kurzbz} : Promise.reject({handled: true})) .then(result => { - return this.deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) + 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); }, @@ -178,9 +196,12 @@ export default { this.studiensemester_kurzbz = studiensemester_kurzbz; + // default Stundensätze from config this.defaultFormDataValues.stunden = this.getDefaultStunden(projekttyp_kurzbz); this.defaultFormDataValues.stundensatz = this.config.defaultProjektbetreuerStundensatz; + this.resetModes(); + // get other initial data this.$api .call(ApiStvProjektbetreuer.getBetreuerarten()) .then(result => { @@ -209,6 +230,7 @@ export default { this.resetForm(); } }, + // confirming Betreuer means adding/updating him in list (but not yet saving in db) confirmProjektbetreuer() { if (!this.betreuerFormOpened) return; @@ -217,11 +239,9 @@ export default { this.$refs.projektbetreuerTable.tabulator.addData(this.addAutoCompleteBetreuerToFormData(this.formData)); } else { this.$refs.projektbetreuerTable.tabulator.updateData([this.formData]); - this.statusNew = true; } - this.newMode = false; - this.editMode = false; + this.resetModes(); }, confirmProjektbetreuerAfterValidation() { //if (!this.formDataModified()) return; @@ -239,14 +259,6 @@ export default { ApiStvProjektbetreuer.saveProjektbetreuer(projektarbeit_id, this.$refs.projektbetreuerTable.tabulator.getData()) ); }, - deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) { - return this.$api - .call(ApiStvProjektbetreuer.deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz)) - .then(response => { - this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); - }) - .catch(this.$fhcAlert.handleSystemError) - }, searchBetreuer(event) { if (this.abortController.betreuer) { this.abortController.betreuer.abort(); @@ -259,6 +271,7 @@ export default { this.filteredBetreuer = result.data; }); }, + // validate betreuer for data validateProjektbetreuer() { let alleBetreuer = this.$refs.projektbetreuerTable.tabulator.getData(); @@ -270,10 +283,15 @@ export default { }, resetForm() { this.formData = this.getDefaultFormData(); - this.projektarbeitDownload = null; + this.beurteilungDownloadLink = null; this.autocompleteSelectedBetreuer = null; this.initialFormData = null; if (this.projekttyp_kurzbz) this.setDefaultStunden(this.projekttyp_kurzbz); + this.disableVertragFields(false); + }, + resetModes() { + this.newMode = false; + this.editMode = false; }, getDefaultFormData() { let formData = {betreuerart_kurzbz : null, note: null}; @@ -287,6 +305,7 @@ export default { 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) { @@ -298,6 +317,7 @@ export default { } return betreuerListe; }, + // add the betreuer selected in automomplete to betreuer liste addAutoCompleteBetreuerToFormData() { let preparedFormData = this.formData; @@ -311,6 +331,7 @@ export default { return preparedFormData; }, + // get default values for stunden getDefaultStunden(projekttyp_kurzbz) { let stunden = '0.0'; if (projekttyp_kurzbz == 'Bachelor') stunden = this.config.defaultProjektbetreuerStunden; @@ -319,8 +340,10 @@ export default { }, 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()) this.formData.stunden = this.getDefaultStunden(projekttyp_kurzbz); }, + // get a new betreuer id (max + 1) getNewBetreuerId() { let max = 0; @@ -330,6 +353,7 @@ export default { return max + 1; }, + // check if form data has been modified since initial data has been captured formDataModified() { if (this.autocompleteSelectedBetreuer != null) return true; @@ -351,22 +375,27 @@ export default { 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.person_id)) .then(response => { + // set the new person in autocomplete field this.autocompleteSelectedBetreuer = response.data; }) .catch(this.$fhcAlert.handleSystemError) + }, + // disable fields which are dependent on Vertrag status + disableVertragFields(disabled) { + this.vertragFieldsDisabled = disabled; } }, template: `
-
+
{{this.$p.t('projektarbeit','betreuerGross')}} -
@@ -448,6 +478,7 @@ export default { type="text" name="stunden" :label="$p.t('projektarbeit', 'stunden')" + :disabled="vertragFieldsDisabled" v-model="formData.stunden" > @@ -459,6 +490,7 @@ export default { type="text" name="stundensatz" :label="$p.t('projektarbeit', 'stundensatz')" + :disabled="vertragFieldsDisabled" v-model="formData.stundensatz" > @@ -469,11 +501,23 @@ export default { - +
+ + {{ autocompleteSelectedBetreuer?.person_id && (!beurteilungDownloadLink || beurteilungDownloadLink == '') ? $p.t('projektarbeit', 'projektarbeitNochNichtBeurteilt') : ''}} +
- @@ -485,8 +529,7 @@ export default { + v-if="autocompleteSelectedBetreuer && autocompleteSelectedBetreuer.person_id">
-
+
-
+
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index fd796e3e1..9b02f9b01 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -84,13 +84,16 @@ export default { frozen: true }, ], - layout: 'fitDataFill', + //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: [ @@ -99,6 +102,13 @@ export default { handler: async() => { await this.$p.loadCategory(['global', 'person', 'stv', 'projektarbeit', 'ui']); } + }, + { + event: 'rowSelected', + handler: row => { + let data = row.getData(); + this.actionEditProjektbetreuer(data.projektarbeit_id, data.person_id, data.betreuerart_kurzbz); + } } ], formData: { @@ -156,7 +166,7 @@ export default { this.formData = betreuer; // set download link - if (betreuer.beurteilungDownloadLink) this.beurteilungDownloadLink = betreuer.beurteilungDownloadLink + if (betreuer.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = betreuer.beurteilungDownloadLink; // set betreuer for autocomplete field this.autocompleteSelectedBetreuer = { @@ -279,15 +289,16 @@ export default { alleBetreuer.push(this.addAutoCompleteBetreuerToFormData(this.formData)); } - return this.$api.call(ApiStvProjektbetreuer.validateProjektbetreuer(alleBetreuer)); + return this.$refs.formProjektbetreuer.call(ApiStvProjektbetreuer.validateProjektbetreuer(alleBetreuer)); }, resetForm() { this.formData = this.getDefaultFormData(); - this.beurteilungDownloadLink = null; + 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; @@ -417,7 +428,7 @@ export default { type="autocomplete" optionLabel="name" v-model="autocompleteSelectedBetreuer" - name="betreuer" + name="person_id" :suggestions="filteredBetreuer" @complete="searchBetreuer" :min-length="3" @@ -501,13 +512,13 @@ export default { -
+
- {{ autocompleteSelectedBetreuer?.person_id && (!beurteilungDownloadLink || beurteilungDownloadLink == '') ? $p.t('projektarbeit', 'projektarbeitNochNichtBeurteilt') : ''}} + {{ autocompleteSelectedBetreuer?.person_id && beurteilungDownloadLink === '' ? $p.t('projektarbeit', 'projektarbeitNochNichtBeurteilt') : ''}}
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js index 1f494d1ec..074d22bde 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js @@ -125,36 +125,22 @@ export default{
{{ betreuerProjektarbeit?.betreuerart_kurzbz && betreuerProjektarbeit?.vertrag_id == null ? ' – '+$p.t('lehre', 'nochKeinVertrag') : '' }}
-
- -
- {{$p.t('lehre', 'vertragurfassung')}}
- - +
+ {{ $p.t('lehre', 'vertragsstatus') }}: {{ vertragsstatus }} +
- - +
+ {{$p.t('lehre', 'vertragurfassung')}} +
+
+
+
+ {{ $p.t('lehre', 'semesterstunden') }}: {{ data.vertragsstunden }} +
+ {{ $p.t('lehre', 'studiensemester') }}: {{ data.vertragsstunden_studiensemester_kurzbz }} +
@@ -164,7 +150,7 @@ export default{ :disabled="vertragsstatus == vertragsstatus_storniert" @click="cancelVertrag" > - {{ $p.t('lehre', 'stornieren') }} + {{ $p.t('lehre', 'vertragStornieren') }}
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index db16dc247..24b37f594 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -43778,7 +43778,7 @@ and represent the current state of research on the topic. The prescribed citatio array( 'app' => 'core', 'category' => 'lehre', - 'phrase' => 'stornieren', + 'phrase' => 'vertragStornieren', 'insertvon' => 'system', 'phrases' => array( array( @@ -43789,7 +43789,7 @@ and represent the current state of research on the topic. The prescribed citatio ), array( 'sprache' => 'English', - 'text' => 'Cancel', + 'text' => 'Cancel contract', 'description' => '', 'insertvon' => 'system' ) From 5bda4db59218977c6f2fd95f841092a1eaab14d8 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Fri, 11 Jul 2025 07:33:10 +0200 Subject: [PATCH 046/312] unr beim kopieren setzen gruppen anzeige direktinskription rausgefillert datatree aufklappen bug gefixed kurzbz bei den icons statt eigene spalte --- .../api/frontend/v1/lv/Lehreinheit.php | 1 + .../education/Lehreinheitgruppe_model.php | 8 +++++- .../js/components/LVVerwaltung/Setup/Table.js | 28 ++++++++++--------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/Lehreinheit.php b/application/controllers/api/frontend/v1/lv/Lehreinheit.php index 0c42c0082..d58489453 100644 --- a/application/controllers/api/frontend/v1/lv/Lehreinheit.php +++ b/application/controllers/api/frontend/v1/lv/Lehreinheit.php @@ -215,6 +215,7 @@ class Lehreinheit extends FHCAPI_Controller $lehreinheit_id_new = getData($insert_result); + $this->_ci->LehreinheitModel->update(array('lehreinheit_id' => $lehreinheit_id_new), array('unr' => $lehreinheit_id_new)); if (in_array($art, array('gruppen', 'alle'))) { $gruppen_result = $this->_ci->LehreinheitgruppeModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id)); diff --git a/application/models/education/Lehreinheitgruppe_model.php b/application/models/education/Lehreinheitgruppe_model.php index 8fc8e09f3..bca395f31 100644 --- a/application/models/education/Lehreinheitgruppe_model.php +++ b/application/models/education/Lehreinheitgruppe_model.php @@ -470,6 +470,12 @@ class Lehreinheitgruppe_model extends DB_Model END AS verplant"); $this->addJoin('tbl_studiengang', 'studiengang_kz', 'LEFT'); $this->addJoin('public.tbl_gruppe', 'gruppe_kurzbz', 'LEFT'); - return $this->loadWhere(array('lehreinheit_id' => $lehreinheit_id)); + + $this->db->where('lehreinheit_id', $lehreinheit_id); + $this->db->group_start() + ->where('tbl_gruppe.direktinskription !=', true) + ->or_where('tbl_gruppe.direktinskription IS NULL') + ->group_end(); + return $this->load(); } } diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index 34a3b3399..a1b2830f1 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -168,7 +168,7 @@ export default { headerFilterFunc: extendedHeaderFilter, }, layout: 'fitDataStretch', - persistenceID: 'lehrveranstaltungen_2025_07_07_v1', + persistenceID: 'lehrveranstaltungen_2025_07_11_v1', selectableRowsRangeMode: 'click', selectableRows: true, rowContextMenu: (component, e) => { @@ -225,11 +225,19 @@ export default { formatter: (cell, formatterParams) => { const rowData = cell.getRow().getData(); const iconKey = (rowData.lehrtyp_kurzbz || '').toLowerCase(); + const lvkurzbz = (cell.getValue()).toUpperCase(); + const parentspan = document.createElement('span'); const span = document.createElement('span'); + + span.classList.add('lv_table_icon', `icon-${iconKey}`); span.title = iconKey || 'LV-Teil'; - return span; + + parentspan.appendChild(span); + parentspan.appendChild(document.createTextNode(` ${lvkurzbz}`)); + + return parentspan }, cellClick: (e, cell) => { @@ -300,16 +308,6 @@ export default { }, width: 150, }, - { - title: this.$p.t('lehre', 'kurzbz'), - field: "lv_kurzbz_anzeige", - headerFilterFuncParams: {field: 'lv_kurzbz_anzeige'}, - formatter: (cell, formatterParams) => { - let rowData = cell.getRow().getData(); - return rowData?.lv_kurzbz?.toUpperCase(); - }, - headerFilter: true - }, { title: this.$p.t('lehre', 'lehrveranstaltung_id'), field: "lehrveranstaltung_id", @@ -667,7 +665,11 @@ export default { if (level === this.currentTreeLevel - 1 ) { row._row.modules.dataTree.open = true; - lastMatchingRow = row; + + if (row._row.data._children?.length > 0) + { + lastMatchingRow = row; + } } }); From 12cdf5a83ef4c2b0e09fbefd25b32e638f8a756d Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Fri, 11 Jul 2025 17:11:19 +0200 Subject: [PATCH 047/312] =?UTF-8?q?accepting=20documents:=20enabled=20acce?= =?UTF-8?q?pting=20for=20all=20Studieng=C3=A4nge,=20not=20just=20master,?= =?UTF-8?q?=20added=20date=20and=20insertamum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/dokument.class.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/include/dokument.class.php b/include/dokument.class.php index 6dec9a66d..7b1d490fb 100644 --- a/include/dokument.class.php +++ b/include/dokument.class.php @@ -888,9 +888,10 @@ class dokument extends basis_db * Akzeptiert ein bestimmtes Dokument * @param char $dokument_kurzbz Bezeichner Dokument. * @param int $person_id Personenkennzeichen. + * @param array $studiengang_typen einschränken nach Studiengang Typ. * @return boolean true wenn akzeptiert bzw geprüft ohne Akzeptieren, false wenn Fehler */ - public function akzeptiereDokument($dokument_kurzbz, $person_id) + public function akzeptiereDokument($dokument_kurzbz, $person_id, $studiengang_typen = null) { $db = new basis_db(); $arrayDoksZuAkzeptieren = array(); @@ -902,7 +903,6 @@ class dokument extends basis_db tbl_prestudent ps, tbl_studiengang sg WHERE ps.studiengang_kz = sg.studiengang_kz - AND sg.typ = 'm' AND person_id = ".$this->db_add_param($person_id)." AND not exists( SELECT * @@ -910,6 +910,11 @@ class dokument extends basis_db where dok.prestudent_id = ps.prestudent_id and dokument_kurzbz = ".$this->db_add_param($dokument_kurzbz).")"; + if (isset($studiengang_typen) && is_array($studiengang_typen) && !empty($studiengang_typen)) + { + $qry .= ' AND sg.typ IN ('. $db->db_implode4SQL($studiengang_typen).')'; + } + //gibt ein Array von zu akzeptierenden Dokumenten zurück if ($db->db_query($qry)) { @@ -923,11 +928,14 @@ class dokument extends basis_db } //für alle prestudent_ids das Dokument akzeptieren - $qry = "INSERT INTO public.tbl_dokumentprestudent(dokument_kurzbz, prestudent_id) VALUES"; + $qry = "INSERT INTO public.tbl_dokumentprestudent(dokument_kurzbz, prestudent_id, datum, insertamum) VALUES"; foreach ($arrayDoksZuAkzeptieren as $prestudent_id) { - $qry .= "(".$this->db_add_param($dokument_kurzbz). ",". $prestudent_id. ")"; + $qry .= "(".$this->db_add_param($dokument_kurzbz). + ",".$this->db_add_param($prestudent_id, FHC_INTEGER). + ",".$this->db_add_param(date('Y-m-d')). + ",".$this->db_add_param(strftime('%Y-%m-%d %H:%M')). ")"; if (next($arrayDoksZuAkzeptieren) == true) { From 4eaf71e5c634cfe7a96bdce0b3d5d7a3b423eee6 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 14 Jul 2025 17:01:16 +0200 Subject: [PATCH 048/312] cancel Vertrag: correct error handling --- .../Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js index 074d22bde..c5de5a1fb 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Vertrag.js @@ -96,7 +96,7 @@ export default{ ? {vertrag_id: this.vertrag_id, person_id: this.person_id} : Promise.reject({handled: true})) .then(result => { - this.$api.call(ApiVertrag.cancelVertrag({vertrag_id: this.vertrag_id, person_id: this.person_id})) + return this.$api.call(ApiVertrag.cancelVertrag({vertrag_id: this.vertrag_id, person_id: this.person_id})) }) .then(result => { this.resetForm(); From 1f258c84d4d01aa4db7df27480d8cea3042b7772 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Tue, 15 Jul 2025 18:56:49 +0200 Subject: [PATCH 049/312] Studierendenverwaltung Projektarbeit: moved Beurteilung download to table actions, separate tabs for details and Betreuer --- .../Details/Projektarbeit/Projektarbeit.js | 34 +++++-- .../Details/Projektarbeit/Projektbetreuer.js | 88 +++++++++++++------ 2 files changed, 87 insertions(+), 35 deletions(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js index dd7c4913c..76953867f 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -348,19 +348,39 @@ export default { - + -
-
- - + + +
+
+
+
+ + +
+
-
- + +
+
+
+ +
+
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index 9b02f9b01..183c81bc0 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -51,7 +51,8 @@ export default { {title: "Vertrag ID", field: "vertrag_id", visible: false}, {title: "Projektarbeit ID", field: "projektarbeit_id", visible: false}, { - title: 'Aktionen', field: 'actions', + title: 'Aktionen', + field: 'actions', minWidth: 150, // Ensures Action-buttons will be always fully displayed formatter: (cell, formatterParams, onRendered) => { let container = document.createElement('div'); @@ -62,6 +63,8 @@ export default { button.innerHTML = ''; 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); }); @@ -71,13 +74,41 @@ export default { button.className = 'btn btn-outline-secondary btn-action'; button.innerHTML = ''; button.title = this.$p.t('ui', 'loeschen'); - button.addEventListener('click', () => { + 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); - //container.append(cell.getData().actionDiv); + 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.addEventListener('click', (event) => { + event.stopPropagation(); + event.preventDefault(); + }); + } + else { + button = document.createElement('a'); + button.setAttribute('href', data.beurteilungDownloadLink); + button.setAttribute('role', 'button'); + button.innerHTML = ''; + 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; }, @@ -404,7 +435,7 @@ export default { template: `
-
+
{{this.$p.t('projektarbeit','betreuerGross')}} @@ -484,27 +515,28 @@ export default {
- - -
- -
- - +
+ + +
+
+ + +
@@ -512,17 +544,17 @@ export default { -
+
-
+
Date: Tue, 15 Jul 2025 23:08:12 +0200 Subject: [PATCH 050/312] Studierendenverwaltung archiv: removed tabulator columns from computed --- .../Stv/Studentenverwaltung/Details/Archiv.js | 91 ------------------- 1 file changed, 91 deletions(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js index 41bd222b8..d02b64c18 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -67,97 +67,6 @@ export default { }; }, computed: { - tabulatorColumns() { - const columns = [ - {title: "Akte Id", field: "akte_id", visible: false}, - {title: this.$p.t('stv', 'archiv_title'), field: "titel"}, - {title: this.$p.t('stv', 'archiv_description'), field: "bezeichnung"}, - {title: this.$p.t('stv', 'archiv_creation_date'), field: "erstelltam"}, - { - title: this.$p.t('stv', 'archiv_signiert'), - field: "signiert", - formatter:"tickCross", - hozAlign:"center", - formatterParams: { - tickElement: '', - crossElement: '' - } - }, - { - title: "Selfservice", - field: "stud_selfservice", - formatter:"tickCross", - hozAlign:"center", - formatterParams: { - tickElement: '', - crossElement: '' - }, - }, - {title: this.$p.t('stv', 'archiv_accepted_on_at'), field: "akzeptiertamum"}, - { - title: this.$p.t('stv', 'archiv_gedruckt'), - field: "gedruckt", - visible: false, - formatter:"tickCross", - hozAlign:"center", - formatterParams: { - tickElement: '', - crossElement: '' - } - }, - { - title: 'Aktionen', field: 'actions', - formatter: (cell, formatterParams, onRendered) => { - let container = document.createElement('div'); - container.className = "d-flex gap-2"; - - let downloadButton = document.createElement('button'); - downloadButton.className = 'btn btn-outline-secondary'; - downloadButton.innerHTML = ''; - downloadButton.title = this.$p.t('ui', 'downloadDok'); - downloadButton.addEventListener('click', evt => { - evt.stopPropagation(); - this.actionDownload(cell.getData().akte_id); - }); - container.append(downloadButton); - - if (this.config.showEdit) - { - let editButton = document.createElement('button'); - editButton.className = 'btn btn-outline-secondary'; - editButton.innerHTML = ''; - editButton.addEventListener('click', () => - this.$refs.edit.open(cell.getData()) - ); - container.append(editButton); - } - - let deleteButton = document.createElement('button'); - deleteButton.className = 'btn btn-outline-secondary'; - deleteButton.innerHTML = ''; - deleteButton.addEventListener('click', evt => { - evt.stopPropagation(); - this.$fhcAlert - .confirmDelete() - .then(result => result ? {akte_id: cell.getData().akte_id, studiengang_kz: this.modelValue.studiengang_kz} : Promise.reject({handled:true})) - .then(this.$fhcApi.factory.stv.archiv.delete) - .then(() => { - //cell.getRow().delete(); - this.reload(); - }) - .catch(this.$fhcAlert.handleSystemError); - }); - container.append(deleteButton); - - return container; - }, - minWidth: 150, // Ensures Action-buttons will be always fully displayed - maxWidth: 150, - frozen: true - } - ]; - return columns; - }, tabulatorOptions() { const options = { ajaxURL: 'dummy', From 1883e8a6f1df3acb10681fd4d90a8bba44e35a5c Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 16 Jul 2025 15:19:50 +0200 Subject: [PATCH 051/312] moved default stunden for projektbetreuer (master/bachelor) to config --- config/global.config-default.inc.php | 6 ++++++ content/student/studentprojektarbeit.js.php | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/config/global.config-default.inc.php b/config/global.config-default.inc.php index 8cfc36bfb..f24a302cc 100644 --- a/config/global.config-default.inc.php +++ b/config/global.config-default.inc.php @@ -162,6 +162,12 @@ define('CIS_LV_LEKTORINNENZUTEILUNG_VERTRAGSPRUEFUNG_VON', ''); // Legt fest, ob Vertragsdetails zum Projektauftrag im Reiter Projektarbeit angezeigt werden define('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN', false); +// Legt default Stunden pro Projektarbeitsbetreuung für Bachelorarbeiten fest +define('FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR', '5.0'); + +// Legt default Stunden pro Projektarbeitsbetreuung für Masterarbeiten fest +define('FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_MASTER', '5.0'); + // Anzeigeoptionen für LV-Plan Menü define('CIS_LVPLAN_EXPORT_ANZEIGEN',true); define('CIS_LVPLAN_PERSONENAUSWAHL_ANZEIGEN',true); diff --git a/content/student/studentprojektarbeit.js.php b/content/student/studentprojektarbeit.js.php index 6c3919327..2005d85fc 100644 --- a/content/student/studentprojektarbeit.js.php +++ b/content/student/studentprojektarbeit.js.php @@ -1165,12 +1165,14 @@ function StudentProjektbetreuerDetailReset() if(document.getElementById('student-projektarbeit-menulist-projekttyp').value=='Diplom') { - document.getElementById('student-projektbetreuer-textbox-stunden').value='5.0'; + document.getElementById('student-projektbetreuer-textbox-stunden').value= + ; document.getElementById('student-projektbetreuer-menulist-betreuerart').value='Erstbegutachter'; } else if(document.getElementById('student-projektarbeit-menulist-projekttyp').value=='Bachelor') { - document.getElementById('student-projektbetreuer-textbox-stunden').value='4.0'; + document.getElementById('student-projektbetreuer-textbox-stunden').value= + ; document.getElementById('student-projektbetreuer-menulist-betreuerart').value='Begutachter'; } else if(document.getElementById('student-projektarbeit-menulist-projekttyp').value=='Praktikum') From 9fa23a1471712f8e3172b196e2b66c0d9ec161e9 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 17 Jul 2025 16:21:23 +0200 Subject: [PATCH 052/312] fixed check permission --- application/controllers/api/frontend/v1/lv/DirektGruppe.php | 4 +++- application/controllers/api/frontend/v1/lv/Gruppe.php | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/DirektGruppe.php b/application/controllers/api/frontend/v1/lv/DirektGruppe.php index fe1737f6a..2dcf7d3cb 100644 --- a/application/controllers/api/frontend/v1/lv/DirektGruppe.php +++ b/application/controllers/api/frontend/v1/lv/DirektGruppe.php @@ -99,7 +99,9 @@ class DirektGruppe extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - $oe_array = $result; + $oe_array = []; + if (hasData($result)) + $oe_array = getData($result); if (!$this->_ci->permissionlib->isBerechtigtMultipleOe('admin', $oe_array, 'suid') && !$this->_ci->permissionlib->isBerechtigtMultipleOe('assistenz', $oe_array, 'suid')) diff --git a/application/controllers/api/frontend/v1/lv/Gruppe.php b/application/controllers/api/frontend/v1/lv/Gruppe.php index 09f253a59..4a1e68656 100644 --- a/application/controllers/api/frontend/v1/lv/Gruppe.php +++ b/application/controllers/api/frontend/v1/lv/Gruppe.php @@ -181,7 +181,10 @@ class Gruppe extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - $oe_array = $result; + + $oe_array = []; + if (hasData($result)) + $oe_array = getData($result); if (!$this->_ci->permissionlib->isBerechtigtMultipleOe('admin', $oe_array, 'suid') && !$this->_ci->permissionlib->isBerechtigtMultipleOe('assistenz', $oe_array, 'suid') && From 75adcefd5168f2b3da82d7fa19aaeb7eb6343821 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 21 Jul 2025 17:52:21 +0200 Subject: [PATCH 053/312] Studentenverwaltung Projektarbeit: made text input possible for all date input --- .../Studentenverwaltung/Details/Projektarbeit/Details.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js index e51828f98..2fdc9d996 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js @@ -350,7 +350,10 @@ export default { 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" > @@ -361,7 +364,10 @@ export default { 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" > @@ -375,7 +381,10 @@ export default { 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" > From 96cb546b68477da2ef7d4ab2ab72ebb600db95aa Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 23 Jul 2025 14:14:03 +0200 Subject: [PATCH 054/312] permission check bug fixed --- application/controllers/api/frontend/v1/lv/Lehreinheit.php | 4 +++- application/controllers/api/frontend/v1/lv/Lektor.php | 4 +++- application/models/ressource/Stundenplandev_model.php | 5 +++-- public/js/api/lehrveranstaltung/gruppe.js | 2 +- public/js/components/LVVerwaltung/Lektor/Daten.js | 1 + 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/Lehreinheit.php b/application/controllers/api/frontend/v1/lv/Lehreinheit.php index d58489453..fd1c23667 100644 --- a/application/controllers/api/frontend/v1/lv/Lehreinheit.php +++ b/application/controllers/api/frontend/v1/lv/Lehreinheit.php @@ -414,7 +414,9 @@ class Lehreinheit extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - $oe_array = $result; + $oe_array = []; + if (hasData($result)) + $oe_array = getData($result); if (!$this->_ci->permissionlib->isBerechtigtMultipleOe('admin', $oe_array, 'suid') && !$this->_ci->permissionlib->isBerechtigtMultipleOe('assistenz', $oe_array, 'suid') && diff --git a/application/controllers/api/frontend/v1/lv/Lektor.php b/application/controllers/api/frontend/v1/lv/Lektor.php index 332bc47b6..4a244c1a1 100644 --- a/application/controllers/api/frontend/v1/lv/Lektor.php +++ b/application/controllers/api/frontend/v1/lv/Lektor.php @@ -284,7 +284,9 @@ class Lektor extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - $oe_array = $result; + $oe_array = []; + if (hasData($result)) + $oe_array = getData($result); return $this->checkPermissionGenerel($permissions, $oe_array); } diff --git a/application/models/ressource/Stundenplandev_model.php b/application/models/ressource/Stundenplandev_model.php index 82b3779ab..e718ba073 100644 --- a/application/models/ressource/Stundenplandev_model.php +++ b/application/models/ressource/Stundenplandev_model.php @@ -223,13 +223,14 @@ class Stundenplandev_model extends DB_Model public function deleteLektorPlanning($lehreinheit_id, $mitarbeiter_uid) { - $this->addDistinct('mitarbeiter_uid'); + //TODO (david) prüfen ob der check notwendig ist + /*$this->addDistinct('mitarbeiter_uid'); $this->addSelect('mitarbeiter_uid'); $stundenplan_result = $this->loadWhere(array('lehreinheit_id' => $lehreinheit_id)); $stundenplan_array = hasData($stundenplan_result) ? (getData($stundenplan_result)) : array(); if (sizeof($stundenplan_array) <= 1) - return error('Diese/r LektorIn kann nicht aus dem LVPlan entfernt werden da dies der/die letzte verplante LektorIn ist'); + return error('Diese/r LektorIn kann nicht aus dem LVPlan entfernt werden da dies der/die letzte verplante LektorIn ist');*/ $this->addJoin('lehre.tbl_stundenplan_betriebsmittel', 'stundenplandev_id'); $betriebsmittel_result = $this->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'tbl_stundenplandev.mitarbeiter_uid' => $mitarbeiter_uid)); diff --git a/public/js/api/lehrveranstaltung/gruppe.js b/public/js/api/lehrveranstaltung/gruppe.js index 12c55509c..e22fdd847 100644 --- a/public/js/api/lehrveranstaltung/gruppe.js +++ b/public/js/api/lehrveranstaltung/gruppe.js @@ -30,7 +30,7 @@ export default { { return { method: 'post', - url: '/api/frontend/v1/lv/gruppe/deleteLVPlan/', + url: '/api/frontend/v1/lv/gruppe/deleteFromLVPlan/', params: deleteData }; }, diff --git a/public/js/components/LVVerwaltung/Lektor/Daten.js b/public/js/components/LVVerwaltung/Lektor/Daten.js index bb40f56fe..0f6b9e0f7 100644 --- a/public/js/components/LVVerwaltung/Lektor/Daten.js +++ b/public/js/components/LVVerwaltung/Lektor/Daten.js @@ -253,6 +253,7 @@ export default{ Date: Tue, 29 Jul 2025 16:46:12 +0200 Subject: [PATCH 055/312] Archivierung von MC Zertifikaten/Zeugnissen --- content/student/studentoverlay.js.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/content/student/studentoverlay.js.php b/content/student/studentoverlay.js.php index d2030f493..d8cb1fc56 100644 --- a/content/student/studentoverlay.js.php +++ b/content/student/studentoverlay.js.php @@ -3553,6 +3553,15 @@ function StudentZeugnisDokumentArchivieren() xml = 'abschlussdokument_lehrgaenge.xml.php'; break; + case 'microcredentialzertifikat_1': + case 'microcredentialzertifikat_2': + case 'microcredentialzertifikat_3': + case 'microcredential_1': + case 'microcredential_2': + case 'microcredential_3': + xml = 'microcredential.xml.php'; + break; + default: alert('Das Archivieren fuer diesen Dokumenttyp wird derzeit nicht unterstuetzt'); return @@ -4783,7 +4792,7 @@ function StudentNotenMoveFromAntrag() var uid = document.getElementById('student-detail-textbox-uid').value; req.add('student_uid', uid); - + var txt = "?"; for(var q in req.parms) { txt = txt+'&'+req.parms[q].name+'='+encodeURIComponent(req.parms[q].value); From f9f0748b89c1b83cb82a354892ae567bf1ded1f5 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 31 Jul 2025 09:10:11 +0200 Subject: [PATCH 056/312] studiensemester_kurzbz aus dem fronted beruecksichtigen extendedHeaderFilter suche in mehreren spalten moeglich getAllOe sql abfrage richtig gestellt --- .../api/frontend/v1/Lehrveranstaltung.php | 38 ++++-- .../models/education/Lehreinheit_model.php | 6 + .../education/Lehrveranstaltung_model.php | 16 ++- public/js/api/lehrveranstaltung.js | 20 +++- public/js/apps/LVVerwaltung.js | 66 ++++++++++- .../components/LVVerwaltung/LVVerwaltung.js | 112 ++++++++++-------- .../js/components/LVVerwaltung/Setup/Table.js | 64 ++-------- .../tabulator/filters/extendedHeaderFilter.js | 11 ++ 8 files changed, 201 insertions(+), 132 deletions(-) diff --git a/application/controllers/api/frontend/v1/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/Lehrveranstaltung.php index 098d44712..2e254bfc8 100644 --- a/application/controllers/api/frontend/v1/Lehrveranstaltung.php +++ b/application/controllers/api/frontend/v1/Lehrveranstaltung.php @@ -27,8 +27,8 @@ class Lehrveranstaltung extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'loadByEmployee' => ['admin:r', 'assistenz:r'], - 'loadByStudiengang' => ['admin:r', 'assistenz:r'], + 'getByEmp' => ['admin:r', 'assistenz:r'], + 'getByStg' => ['admin:r', 'assistenz:r'], 'loadByLV' => ['admin:r', 'assistenz:r'], ]); @@ -37,6 +37,7 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->_ci->load->model('education/Lehreinheit_model', 'LehreinheitModel'); $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); $this->_ci->load->library('VariableLib', ['uid' => $this->_uid]); @@ -46,12 +47,14 @@ class Lehrveranstaltung extends FHCAPI_Controller ) ); } - public function loadByEmployee($mitarbeiter_uid = null, $stg_kz = null) - { - if (is_null($mitarbeiter_uid)) - $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); - $studiensemester_kurzbz = $this->_ci->variablelib->getVar('semester_aktuell'); + public function getByEmp($studiensemester_kurzbz = null, $mitarbeiter_uid = null, $stg_kz = null) + { + + if (is_null($mitarbeiter_uid)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $lehrveranstaltungen = $this->_ci->LehreinheitModel->getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $stg_kz); $lehrveranstaltungen_data = $this->getDataOrTerminateWithError($lehrveranstaltungen); @@ -72,8 +75,7 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->terminateWithSuccess($tree); } - - public function loadByStudiengang($studiengang_kz = null, $semester = null) + public function getByStg($studiensemester_kurzbz = null, $studiengang_kz = null, $semester = null) { if (is_null($studiengang_kz) || !preg_match("/^-?[1-9][0-9]*$/", (string)$studiengang_kz)) $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); @@ -87,7 +89,7 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); - $studiensemester_kurzbz = $this->_ci->variablelib->getVar('semester_aktuell'); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $studienplan_data = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $semester, $verband); $studienplan_ids = array(); @@ -107,7 +109,6 @@ class Lehrveranstaltung extends FHCAPI_Controller $lehrveranstaltungen_data = $this->_ci->LehrveranstaltungModel->getLvsByStudiengang($studienplan_ids, $placeholders, $only_ids, $studiengang_kz, $studiensemester_kurzbz, $semester, $verband); $lehrveranstaltungen_data = hasData($lehrveranstaltungen_data) ? getData($lehrveranstaltungen_data) : array(); - $tree = []; foreach ($lehrveranstaltungen_data as $row) { @@ -246,4 +247,19 @@ class Lehrveranstaltung extends FHCAPI_Controller } } } + + private function getStudiensemesterKurzbz($studiensemester_kurzbz = null) + { + if (!is_null($studiensemester_kurzbz)) + { + $studiensemester_result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz); + + if (isError($studiensemester_result) || !hasData($studiensemester_result)) + $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + return getData($studiensemester_result)[0]->studiensemester_kurzbz; + } + + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + } } diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index ce414bad2..c33b87ad4 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -356,6 +356,8 @@ EOSQL; gruppen.gruppen, mitarbeiter.lektoren, mitarbeiter.le_planstunden, + mitarbeiter.vorname, + mitarbeiter.nachname, mitarbeiter.semesterstunden, fachbereich.bezeichnung as fachbereich, UPPER(CONCAT(tbl_studiengang.typ,tbl_studiengang.kurzbz)) as studiengang, @@ -659,11 +661,15 @@ EOSQL; SELECT tbl_lehreinheitmitarbeiter.lehreinheit_id, STRING_AGG(m.kurzbz, ' ') AS lektoren, + STRING_AGG(tbl_person.vorname, ' ') AS vorname, + STRING_AGG(tbl_person.nachname, ' ') AS nachname, STRING_AGG(tbl_lehreinheitmitarbeiter.semesterstunden::text, ' ') AS semesterstunden, STRING_AGG(tbl_lehreinheitmitarbeiter.planstunden::text, ' ') AS le_planstunden FROM lehre.tbl_lehreinheitmitarbeiter JOIN public.tbl_mitarbeiter m USING (mitarbeiter_uid) JOIN lehreinheiten USING(lehreinheit_id) + JOIN public.tbl_benutzer ON mitarbeiter_uid = uid + JOIN public.tbl_person ON tbl_benutzer.person_id = tbl_person.person_id GROUP BY tbl_lehreinheitmitarbeiter.lehreinheit_id )"; } diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index 121de9233..303bebe1e 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -1200,19 +1200,29 @@ class Lehrveranstaltung_model extends DB_Model public function getAllOe($lv_id, $stg_kz = null) { - $qry = "SELECT DISTINCT oe_kurzbz + $qry = "(SELECT DISTINCT oe_kurzbz FROM lehre.tbl_studienplan_lehrveranstaltung JOIN lehre.tbl_studienplan USING(studienplan_id) JOIN lehre.tbl_studienordnung USING(studienordnung_id) JOIN public.tbl_studiengang USING(studiengang_kz) - WHERE lehrveranstaltung_id = ? "; + WHERE lehrveranstaltung_id = ?)"; $params = array($lv_id); if (!is_null($stg_kz)) { - $qry .= ' OR studiengang_kz = ?'; + $qry .= ' UNION + (SELECT oe_kurzbz + FROM public.tbl_studiengang WHERE studiengang_kz = ?)'; $params[] = $stg_kz; } + else + { + $qry .= ' UNION + (SELECT oe_kurzbz + FROM public.tbl_studiengang WHERE studiengang_kz = (SELECT tbl_lehrveranstaltung.studiengang_kz FROM lehre.tbl_lehrveranstaltung WHERE lehrveranstaltung_id = ?)) + '; + $params[] = $lv_id; + } return $this->execReadOnlyQuery($qry, $params); } diff --git a/public/js/api/lehrveranstaltung.js b/public/js/api/lehrveranstaltung.js index 0499572ba..8aa6becaf 100644 --- a/public/js/api/lehrveranstaltung.js +++ b/public/js/api/lehrveranstaltung.js @@ -7,16 +7,24 @@ export default { url: '/api/frontend/v1/Lehrveranstaltung/loadByLV/' + encodeURIComponent(lehrveranstaltung_id) }; }, - getByStg(studiengang_kz, semester) + getByStg(studiensemester_kurzbz, studiengang_kz, semester = null) { - return ("/api/frontend/v1/Lehrveranstaltung/loadByStudiengang/" + encodeURIComponent(studiengang_kz) + "/" + encodeURIComponent(semester)); - }, + let path = "/api/frontend/v1/Lehrveranstaltung/getByStg/" + encodeURIComponent(studiensemester_kurzbz) + "/" + encodeURIComponent(studiengang_kz); - getByEmpStg(mitarbeiter_uid, stg) + if (semester) + path += "/" + encodeURIComponent(semester); + + return path; + }, + getByEmp(studiensemester_kurzbz, mitarbeiter_uid, stg = null) { - return ("/api/frontend/v1/Lehrveranstaltung/loadByEmployee/" + encodeURIComponent(mitarbeiter_uid) + "/" + encodeURIComponent(stg)); - }, + let path = "/api/frontend/v1/Lehrveranstaltung/getByEmp/" + encodeURIComponent(studiensemester_kurzbz) + "/" + encodeURIComponent(mitarbeiter_uid); + if (stg) + path += "/" + encodeURIComponent(stg); + + return path; + }, getTable(url) { return { diff --git a/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index c582f817e..2ab3fd98c 100644 --- a/public/js/apps/LVVerwaltung.js +++ b/public/js/apps/LVVerwaltung.js @@ -1,5 +1,6 @@ import LVVerwaltung from "../components/LVVerwaltung/LVVerwaltung.js"; import Phrasen from "../plugins/Phrasen.js"; +import {DEFAULT_MODE_RAUMINFO} from "../components/Cis/Mylv/RoomInformation"; const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router; @@ -13,8 +14,35 @@ const router = VueRouter.createRouter({ }, { name: `byEmp`, - path: `/emp/:emp/:stg?`, - component: LVVerwaltung + path: `/emp/:studiensemester_kurzbz/:emp/:stg?/:semester?`, + component: LVVerwaltung, + props: route => { + let {emp, stg, semester, studiensemester_kurzbz} = route.params; + + if (emp === '') + emp = undefined; + + if (stg === '') + stg = undefined; + + if (studiensemester_kurzbz === '') + studiensemester_kurzbz = undefined; + + return { + studiensemester_kurzbz: studiensemester_kurzbz, + emp: emp, + stg: stg, + }; + }, + beforeEnter: (to, from, next) => { + const { studiensemester_kurzbz } = to.params; + const isSemester = /^(SS|WS)\d{4}$/.test(studiensemester_kurzbz); + + if (!isSemester) + return next({ path: '/' }); + else + next(); + } }, /*{ name: `byFachbereich`, @@ -23,13 +51,41 @@ const router = VueRouter.createRouter({ },*/ { name: `byStg`, - path: `/stg/:stg/:semester?`, - component: LVVerwaltung + path: '/stg/:studiensemester_kurzbz/:stg?/:semester?/', + component: LVVerwaltung, + props: route => { + let { studiensemester_kurzbz, stg, semester } = route.params; + + if (semester === '') + semester = undefined; + + if (studiensemester_kurzbz === '') + studiensemester_kurzbz = undefined; + + if (stg === '') + semester = undefined; + + return { + studiensemester_kurzbz: studiensemester_kurzbz, + stg: stg, + semester: semester != null ? Number(semester) : null, + }; + }, + beforeEnter: (to, from, next) => { + const studiensemester_kurzbz = to.params?.studiensemester_kurzbz + const isSemester = /^(SS|WS)\d{4}$/.test(studiensemester_kurzbz); + + if (!isSemester) + return next({ path: '/' }); + else + next(); + } }, { path: '/:pathMatch(.*)*', redirect: '/' - } + }, + ] }); diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index c7c34802d..7b8921c80 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -26,16 +26,22 @@ export default { defaultSemester: String, lvRoot: String, permissions: Object, - config: Object + config: Object, + stg: { type: String, required: false }, + semester: { type: Number, required: false, default: null }, + studiensemester_kurzbz: { type: String, required: false, default: null }, + emp: { type: String, required: false, default: null } }, computed: { - type(){ - return this.$route.params.type + selectedStudiensemester() { + return this.studiensemester_kurzbz != null + ? this.studiensemester_kurzbz + : this.defaultSemester; } }, provide() { return { - currentSemester: Vue.computed(() => this.studiensemesterKurzbz), + currentSemester: Vue.computed(() => this.selectedStudiensemester), dropdowns: this.dropdowns, configShowVertragsdetails: this.config.showVertragsdetails, configShowGewichtung: this.config.showGewichtung, @@ -49,18 +55,26 @@ export default { } }, mounted() { - this.updateFilter(this.$route); + this.updateFilter(); }, watch: { - '$route'(to) { - this.updateFilter(to); + stg() { + this.updateFilter(); + }, + semester() { + this.updateFilter(); + }, + selectedStudiensemester() { + this.updateFilter(); + }, + emp() { + this.updateFilter(); }, }, data() { return { selected: [], - studiensemesterKurzbz: this.defaultSemester, - stg: "", + studiengang: "", filter: {}, endpoint: ApiStudiengangTree, dropdowns: { @@ -95,20 +109,16 @@ export default { } }, methods: { - updateFilter(route) + updateFilter() { - let filter = { ...route.params, ...route.query }; - if (!filter.activeFilter) - { - if (filter.emp) - { - filter.activeFilter = 'employee'; - } - else if (filter.stg) - { - filter.activeFilter = 'verband'; - } + const filter = { + stg: this.stg, + emp: this.emp, + semester: this.semester, + studiensemester_kurzbz: this.selectedStudiensemester, + activeFilter: this.emp ? 'employee' : this.stg ? 'verband' : null } + this.filter = filter; }, handleRowClicked(data) @@ -117,18 +127,26 @@ export default { }, onSelectEmployee(emp) { - let stg = this.stg === '' ? null : this.stg; + const { stg, semester } = this.filter; - this.$router.push({ - name: 'byEmp', - params: { emp, stg, activeFilter: 'employee'}, - }); + let studiensemester_kurzbz = this.selectedStudiensemester; + const params = { emp }; + + if (stg) + params.stg = stg; + if (semester !== null) + params.semester = semester; + if (studiensemester_kurzbz) + params.studiensemester_kurzbz = studiensemester_kurzbz; + + this.$router.push({ name: 'byEmp', params }) }, onSelectVerband({link}) { let stg = null; let semester = null; + let studiensemester_kurzbz = this.selectedStudiensemester; if (typeof link === 'number') stg = link; @@ -137,28 +155,22 @@ export default { [stg, semester] = link.split('/'); } - this.stg = stg; + const routeName = this.filter.emp ? 'byEmp' : 'byStg'; + const params = { stg }; - if (this.filter && this.filter.emp) - { - this.$router.push({ - name: 'byEmp', - params: { emp: this.filter.emp, stg, activeFilter: 'employee' }, - }); - } - else - { - this.$router.push({ - name: 'byStg', - params: { stg, semester, activeFilter: 'verband' }, - }); - } + if (semester !== null) + params.semester = Number(semester); + if (studiensemester_kurzbz) + params.studiensemester_kurzbz = studiensemester_kurzbz; + if (this.filter.emp) + params.emp = this.filter.emp; + this.$router.push({ name: routeName, params }); this.selected = []; }, resetEmployeeFilter() { - const newParams = { ...this.$route.params, activeFilter: 'verband' }; + const newParams = { ...this.filter, activeFilter: 'verband' }; if (newParams.stg === '') this.$router.replace({ name: 'index' }); else @@ -166,24 +178,24 @@ export default { delete newParams.emp; newParams.semester = null; this.$router.replace({ name: 'byStg', params: newParams }); - } }, searchfunction(params) { return this.$api.call(ApiSearchbar.search(params)); }, studiensemesterChanged(newValue) { - this.studiensemesterKurzbz = newValue; - this.$refs.lvTable.reload(); + const routeName = this.filter.activeFilter === 'employee' ? 'byEmp' : 'byStg'; + const newParams = {...this.filter, studiensemester_kurzbz: newValue}; + this.$router.push({ name: routeName, params: newParams }); this.selected = []; }, }, created() { - if (this.$route.params.stg !== undefined) + if (this.stg !== undefined) { - this.selectedStudiengang = this.$route.params?.semester !== '' && this.$route.params?.semester - ? `${this.$route.params.stg}/${this.$route.params.semester}` - : this.$route.params.stg; + this.selectedStudiengang = this.semester !== '' && this.semester + ? `${this.stg}/${this.semester}` + : this.stg; } this.$p.loadCategory(['lehre', 'person', 'global']) @@ -250,7 +262,7 @@ export default {
- +
diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index a1b2830f1..5b4d45c24 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -61,42 +61,6 @@ export default { }, data() { return { - fieldTitleMap: { - lv_kurzbz: ['lehre', 'kurzbz'], - tags: ['ui', 'tags'], - lehrveranstaltung_id: ['lehre', 'lehrveranstaltung_id'], - lv_bezeichnung: ['ui', 'bezeichnung'], - lv_bezeichnung_english: ['lehre', 'bezeichnungeng'], - lv_studiengang_kz: ['lehre', 'studiengangskennzahlLehre'], - studiengang: ['lehre', 'studiengang'], - semester: ['lehre', 'semester'], - sprache: ['global', 'sprache'], - lv_ects: ['lehre', 'ects'], - semesterstunden: ['lehre', 'semesterstunden'], - anmerkung: ['global', 'anmerkung'], - lehre: ['lehre', 'lehre'], - lehreverzeichnis: ['ui', 'lehreverzeichnis'], - aktiv: ['person', 'aktiv'], - planfaktor: ['ui', 'planfaktor'], - planlektoren: ['ui', 'planlektoren'], - planpersonalkosten: ['ui', 'planpersonalkosten'], - plankostenprolektor: ['ui', 'plankostenprolektor'], - orgform_kurzbz: ['lehre', 'organisationsform'], - studienplan_id: ['ui', 'studienplan_id'], - studienplan_bezeichnung: ['ui', 'studienplan_bezeichnung'], - lehrtyp_kurzbz: ['ui', 'lehrtyp_kurzbz'], - lehrform_kurzbz: ['lehre', 'lehrform'], - le_planstunden: ['lehre', 'leplanstunden'], - lehreinheit_id: ['lehre', 'lehreinheit_id'], - stundenblockung: ['lehre', 'stundenblockung'], - wochenrythmus: ['lehre', 'wochenrhytmus'], - startkw: ['lehre', 'startkw'], - raumtyp: ['lehre', 'raumtyp'], - raumtypalternativ: ['lehre', 'raumtypalternativ'], - gruppen: ['lehre', 'gruppen'], - lektoren: ['lehre', 'lehrende'], - }, - expanded: [], selectedColumnValues: [], tagEndpoint: ApiTag, @@ -168,7 +132,7 @@ export default { headerFilterFunc: extendedHeaderFilter, }, layout: 'fitDataStretch', - persistenceID: 'lehrveranstaltungen_2025_07_11_v1', + persistenceID: 'lehrveranstaltungen_2025_07_31_v1', selectableRowsRangeMode: 'click', selectableRows: true, rowContextMenu: (component, e) => { @@ -348,7 +312,7 @@ export default { {title: this.$p.t('lehre', 'raumtyp'), field: "raumtyp", headerFilter: true, headerFilterFuncParams: {field: 'raumtyp'}, visible: false}, {title: this.$p.t('lehre', 'raumtypalternativ'), field: "raumtypalternativ", headerFilter: true, headerFilterFuncParams: {field: 'raumtypalternativ'}, visible: false}, {title: this.$p.t('lehre', 'gruppen'), field: "gruppen", headerFilter: true, headerFilterFuncParams: {field: 'gruppen'}}, - {title: this.$p.t('lehre', 'lehrende'), field: "lektoren", headerFilter: true, headerFilterFuncParams: {field: 'lektoren'}}, + {title: this.$p.t('lehre', 'lehrende'), field: "lektoren", headerFilter: true, headerFilterFuncParams: {field: ['lektoren', 'vorname', 'nachname']}}, ], } @@ -417,18 +381,14 @@ export default { { if (this.filter.activeFilter === 'employee' && this.filter.emp) { - return this.$api.getUri(ApiLv.getByEmpStg( - this.filter.emp, - this.filter.stg - )); + const { emp, stg, studiensemester_kurzbz } = this.filter; + return this.$api.getUri(ApiLv.getByEmp(studiensemester_kurzbz, emp, stg)); } if (this.filter.activeFilter === 'verband' && this.filter.stg) { - return this.$api.getUri(ApiLv.getByStg( - this.filter.stg, - this.filter.semester - )); + const { stg, semester, studiensemester_kurzbz } = this.filter; + return this.$api.getUri(ApiLv.getByStg(studiensemester_kurzbz, stg, semester)); } }, resetEmployeeFilter() @@ -437,16 +397,6 @@ export default { delete newFilter.emp; newFilter.activeFilter = 'verband'; }, - buildParams() - { - const params = {}; - for (const [key, value] of Object.entries(this.filter)) { - if (value !== undefined && value !== null) { - params[key] = value; - } - } - return params; - }, showLehreinheitModal() { this.resetModal(); this.$refs.lehreinheitModal.show(); @@ -649,7 +599,7 @@ export default { let rootRows = this.$refs.table.tabulator.getRows(true); var lastRow = rootRows[rootRows.length - 1]; - lastRow.treeCollapse(true) + lastRow?.treeCollapse(true) this.currentTreeLevel = 0; }, diff --git a/public/js/tabulator/filters/extendedHeaderFilter.js b/public/js/tabulator/filters/extendedHeaderFilter.js index 5dfcce205..1112d36f3 100644 --- a/public/js/tabulator/filters/extendedHeaderFilter.js +++ b/public/js/tabulator/filters/extendedHeaderFilter.js @@ -41,6 +41,17 @@ function parseFilterExpression(expression) export function extendedHeaderFilter(headerValue, rowValue, rowData, filterParams) { + const fields = Array.isArray(filterParams?.field) + ? filterParams.field + : [filterParams?.field]; + + if (fields.length > 1 && rowData) + { + rowValue = fields + .map(f => rowData[f] ?? '') + .filter(Boolean) + .join(' '); + } if (typeof headerValue === 'boolean') { return rowValue === headerValue; From 4d3ad0008ea3a12d56ca16ff554093768f92cafe Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 31 Jul 2025 09:33:11 +0200 Subject: [PATCH 057/312] deleted automated added import --- public/js/apps/LVVerwaltung.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index 2ab3fd98c..c72f9fc9e 100644 --- a/public/js/apps/LVVerwaltung.js +++ b/public/js/apps/LVVerwaltung.js @@ -1,6 +1,5 @@ import LVVerwaltung from "../components/LVVerwaltung/LVVerwaltung.js"; import Phrasen from "../plugins/Phrasen.js"; -import {DEFAULT_MODE_RAUMINFO} from "../components/Cis/Mylv/RoomInformation"; const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router; From 913b218fcac54aeac6260762769f641795d66fda Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Fri, 1 Aug 2025 10:53:15 +0200 Subject: [PATCH 058/312] phrasenupdate Stundenplan -> LV-Plan; tooltip update/remove; --- system/phrasesupdate.php | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 26472be29..034e4aca5 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31627,12 +31627,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'termine', + 'phrase' => 'termineV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterricht laut Stundenplan', + 'text' => 'Unterricht laut LV-Plan', 'description' => '', 'insertvon' => 'system' ), @@ -31652,7 +31652,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterricht laut Stundenplan', + 'text' => 'Unterricht laut LV-Plan', 'description' => '', 'insertvon' => 'system' ), @@ -32107,12 +32107,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'zeitNichtAusStundenplanBeginn', + 'phrase' => 'zeitNichtAusStundenplanBeginnV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterrichtbeginn entspricht keinem Unterrichtstermin laut Stundenplan.', + 'text' => 'Unterrichtbeginn entspricht keinem Unterrichtstermin laut LV-Plan.', 'description' => '', 'insertvon' => 'system' ), @@ -32127,12 +32127,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'zeitNichtAusStundenplanEnde', + 'phrase' => 'zeitNichtAusStundenplanEndeV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterrichtende entspricht keinem Unterrichtstermin laut Stundenplan.', + 'text' => 'Unterrichtende entspricht keinem Unterrichtstermin laut LV-Plan.', 'description' => '', 'insertvon' => 'system' ), @@ -32147,12 +32147,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'datumNichtAusStundenplan', + 'phrase' => 'datumNichtAusStundenplanV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Datum entspricht keinem Stundenplan Termin.', + 'text' => 'Datum entspricht keinem LV-Plan Termin.', 'description' => '', 'insertvon' => 'system' ), @@ -32323,14 +32323,14 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'tooltipLektorStartKontrolleV2', + 'phrase' => 'tooltipLektorStartKontrolleV4', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Um eine Anwesenheitskontrolle für Ihre ausgewählte Unterrichtsgruppe durchzuführen, wählen Sie bitte einen Termin aus dem Stundenplan aus oder geben händisch die gewünschte Gültigkeitkeitsdauer der Kontrolle an. + 'text' => 'Um eine Anwesenheitskontrolle für Ihre ausgewählte Unterrichtsgruppe durchzuführen, wählen Sie bitte einen Termin aus dem LV-Plan aus oder geben händisch die gewünschte Gültigkeitkeitsdauer der Kontrolle an. - Die Gültigkeitsdauer bestimmt die Gewichtung der Anwesenheit in Relation zum Gesamtausmaß, sie können diese aber nach eigenem Ermessen anpassen und müssen sich nicht streng an die Termine im Stundenplan halten. + Die Gültigkeitsdauer bestimmt die Gewichtung der Anwesenheit in Relation zum Gesamtausmaß, sie können diese aber nach eigenem Ermessen anpassen und müssen sich nicht streng an die Termine im LV-Plan halten. Sie können pro Datum und Unterrichtsgruppe mehrere Anwesenheitskontrollen am Tag durchführen, solange sich diese nicht in Ihren Zeiten überschneiden und eine Mindestlänge von {0} Minuten beträgt.', 'description' => '', @@ -32599,12 +32599,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'termineAusStundenplan', + 'phrase' => 'termineAusStundenplanV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterricht Termine laut Stundenplan', + 'text' => 'Unterricht Termine laut LV-Plan', 'description' => '', 'insertvon' => 'system' ), @@ -32619,12 +32619,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'tooltipUnterrichtZeitCustom', + 'phrase' => 'tooltipUnterrichtZeitCustomV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterrichtszeiten werden aus dem Stundenplan geladen, überschreiben Sie diese nur falls Ihre tatsächliche Unterrichtszeiten davon abweichen!', + 'text' => 'Unterrichtszeiten werden aus dem LV-Plan geladen, überschreiben Sie diese nur falls Ihre tatsächliche Unterrichtszeiten davon abweichen!', 'description' => '', 'insertvon' => 'system' ), @@ -32639,12 +32639,12 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'tooltipUnterrichtDatumCustom', + 'phrase' => 'tooltipUnterrichtDatumCustomV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Unterrichtsdaten werden aus dem Stundenplan geladen, überschreiben Sie dieses nur falls Ihr tatsächliches Unterrichtsdatum abweicht!', + 'text' => 'Unterrichtsdaten werden aus dem LV-Plan geladen, überschreiben Sie dieses nur falls Ihr tatsächliches Unterrichtsdatum abweicht!', 'description' => '', 'insertvon' => 'system' ), @@ -40200,7 +40200,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Stundenplan', + 'text' => 'LV-Plan', 'description' => '', 'insertvon' => 'system' ), From 5d07879ebdfedae988508c96db1a078cd89099a1 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Fri, 1 Aug 2025 11:44:23 +0200 Subject: [PATCH 059/312] org form hinzugefuegt im stree gruppe autocomplete auch auf die bezeichnung getalloe sql angepasst --- .../api/frontend/v1/lv/Lehreinheit.php | 2 +- .../api/frontend/v1/lv/StgTree.php | 26 +++++++++++ .../models/education/Lehreinheit_model.php | 2 +- .../education/Lehrveranstaltung_model.php | 46 +++++++++---------- .../models/organisation/Studienplan_model.php | 2 + public/js/apps/LVVerwaltung.js | 5 +- .../LVVerwaltung/Details/Gruppen.js | 2 +- .../components/LVVerwaltung/LVVerwaltung.js | 6 +-- 8 files changed, 59 insertions(+), 32 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/Lehreinheit.php b/application/controllers/api/frontend/v1/lv/Lehreinheit.php index fd1c23667..b851f8c22 100644 --- a/application/controllers/api/frontend/v1/lv/Lehreinheit.php +++ b/application/controllers/api/frontend/v1/lv/Lehreinheit.php @@ -80,7 +80,7 @@ class Lehreinheit extends FHCAPI_Controller $lehrveranstaltung = getData($lehrveranstaltung_result)[0]; - $oe_result = $this->_ci->LehrveranstaltungModel->getAllOe($lehrveranstaltung->lehrveranstaltung_id, $lehrveranstaltung->studiengang_kz); + $oe_result = $this->_ci->LehrveranstaltungModel->getAllOe($lehrveranstaltung->lehrveranstaltung_id); $oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array(); if (!$this->_ci->permissionlib->isBerechtigtMultipleOe('admin', $oe_array, 'suid') && diff --git a/application/controllers/api/frontend/v1/lv/StgTree.php b/application/controllers/api/frontend/v1/lv/StgTree.php index fe3047902..8272da978 100644 --- a/application/controllers/api/frontend/v1/lv/StgTree.php +++ b/application/controllers/api/frontend/v1/lv/StgTree.php @@ -86,6 +86,32 @@ class StgTree extends FHCAPI_Controller ]); $list = $this->getDataOrTerminateWithError($result); + $result = $this->StudiengangModel->load($studiengang_kz); + $result = $this->getDataOrTerminateWithError($result); + if ($result) + { + if (current($result)->mischform) + { + $this->load->model('organisation/Studienordnung_model', 'StudienordnungModel'); + + $this->StudienordnungModel->addDistinct(); + $this->StudienordnungModel->addSelect("CONCAT(studiengang_kz, '/', p.orgform_kurzbz) AS link"); + $this->StudienordnungModel->addSelect("p.orgform_kurzbz AS name"); + $this->StudienordnungModel->addSelect("TRUE as leaf", false); + + $this->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id'); + + $result = $this->StudienordnungModel->loadWhere([ + 'aktiv' => true, + 'studiengang_kz' => $studiengang_kz, + 'p.orgform_kurzbz !=' => 'DDP' + ]); + $result = $this->getDataOrTerminateWithError($result); + + $list = array_merge($list, $result); + } + } + $this->terminateWithSuccess($list); } } diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index c33b87ad4..c373f5449 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -322,7 +322,7 @@ EOSQL; if (hasData($result)) { $lehrveranstaltung = getData($result)[0]; - $oe_result = $this->LehrveranstaltungModel->getAllOe($lehrveranstaltung->lehrveranstaltung_id, $lehrveranstaltung->studiengang_kz); + $oe_result = $this->LehrveranstaltungModel->getAllOe($lehrveranstaltung->lehrveranstaltung_id); return success(hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array('')); } } diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index 303bebe1e..31ec2901f 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -1161,7 +1161,7 @@ class Lehrveranstaltung_model extends DB_Model } if (!is_null($verband)) { - $qry .= ' AND (orgform_kurzbz = ? OR orgform_kurzbz IS NULL)'; + $qry .= ' AND (tbl_lehrveranstaltung.orgform_kurzbz = ? OR tbl_lehrveranstaltung.orgform_kurzbz IS NULL)'; $params[] = $verband; } @@ -1198,31 +1198,29 @@ class Lehrveranstaltung_model extends DB_Model "; } - public function getAllOe($lv_id, $stg_kz = null) + public function getAllOe($lv_id) { - $qry = "(SELECT DISTINCT oe_kurzbz - FROM lehre.tbl_studienplan_lehrveranstaltung - JOIN lehre.tbl_studienplan USING(studienplan_id) - JOIN lehre.tbl_studienordnung USING(studienordnung_id) - JOIN public.tbl_studiengang USING(studiengang_kz) - WHERE lehrveranstaltung_id = ?)"; + $qry = "SELECT DISTINCT oe_kurzbz + FROM lehre.tbl_studienplan_lehrveranstaltung + JOIN lehre.tbl_studienplan USING(studienplan_id) + JOIN lehre.tbl_studienordnung USING(studienordnung_id) + JOIN public.tbl_studiengang USING(studiengang_kz) + WHERE lehrveranstaltung_id = ? - $params = array($lv_id); - if (!is_null($stg_kz)) - { - $qry .= ' UNION - (SELECT oe_kurzbz - FROM public.tbl_studiengang WHERE studiengang_kz = ?)'; - $params[] = $stg_kz; - } - else - { - $qry .= ' UNION - (SELECT oe_kurzbz - FROM public.tbl_studiengang WHERE studiengang_kz = (SELECT tbl_lehrveranstaltung.studiengang_kz FROM lehre.tbl_lehrveranstaltung WHERE lehrveranstaltung_id = ?)) - '; - $params[] = $lv_id; - } + UNION + + ( + SELECT oe_kurzbz + FROM public.tbl_studiengang + WHERE studiengang_kz = ( + SELECT tbl_lehrveranstaltung.studiengang_kz + FROM lehre.tbl_lehrveranstaltung + WHERE lehrveranstaltung_id = ? + ) + ) + "; + + $params = array($lv_id, $lv_id); return $this->execReadOnlyQuery($qry, $params); } diff --git a/application/models/organisation/Studienplan_model.php b/application/models/organisation/Studienplan_model.php index 5a9097d9a..0b41a8c39 100644 --- a/application/models/organisation/Studienplan_model.php +++ b/application/models/organisation/Studienplan_model.php @@ -107,6 +107,8 @@ class Studienplan_model extends DB_Model )); } + // Deprecated + // im Lehrveranstaltung_model vorhanden public function getAllOesForLv($lehrveranstaltung_id) { $this->addDistinct('oe_kurzbz'); diff --git a/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index c72f9fc9e..f365b74a8 100644 --- a/public/js/apps/LVVerwaltung.js +++ b/public/js/apps/LVVerwaltung.js @@ -31,6 +31,7 @@ const router = VueRouter.createRouter({ studiensemester_kurzbz: studiensemester_kurzbz, emp: emp, stg: stg, + semester: semester }; }, beforeEnter: (to, from, next) => { @@ -62,12 +63,12 @@ const router = VueRouter.createRouter({ studiensemester_kurzbz = undefined; if (stg === '') - semester = undefined; + stg = undefined; return { studiensemester_kurzbz: studiensemester_kurzbz, stg: stg, - semester: semester != null ? Number(semester) : null, + semester: semester, }; }, beforeEnter: (to, from, next) => { diff --git a/public/js/components/LVVerwaltung/Details/Gruppen.js b/public/js/components/LVVerwaltung/Details/Gruppen.js index 62dae172f..219b956d8 100644 --- a/public/js/components/LVVerwaltung/Details/Gruppen.js +++ b/public/js/components/LVVerwaltung/Details/Gruppen.js @@ -103,7 +103,7 @@ export default{ { const query = event.query.toLowerCase().trim(); this.filteredGroups = this.dropdowns.gruppen_array.filter(gruppe => { - return gruppe.gruppe_kurzbz.toLowerCase().includes(query); + return gruppe.gruppe_kurzbz.toLowerCase().includes(query) || gruppe?.bezeichnung?.toLowerCase().includes(query); }).map(gruppe => ({ label: gruppe.bezeichnung ? `${gruppe.gruppe_kurzbz.trim()} (${gruppe.bezeichnung})` diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 7b8921c80..7bfb3ec82 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -28,7 +28,7 @@ export default { permissions: Object, config: Object, stg: { type: String, required: false }, - semester: { type: Number, required: false, default: null }, + semester: { type: [Number, String], required: false, default: null }, studiensemester_kurzbz: { type: String, required: false, default: null }, emp: { type: String, required: false, default: null } }, @@ -159,7 +159,7 @@ export default { const params = { stg }; if (semester !== null) - params.semester = Number(semester); + params.semester = semester; if (studiensemester_kurzbz) params.studiensemester_kurzbz = studiensemester_kurzbz; if (this.filter.emp) @@ -176,7 +176,7 @@ export default { else { delete newParams.emp; - newParams.semester = null; + this.$router.replace({ name: 'byStg', params: newParams }); } }, From be2419063651d3ceac7baafaa14883ca7f8117d6 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 4 Aug 2025 10:15:07 +0200 Subject: [PATCH 060/312] studiensemester auswahl abhaengig von url --- public/js/components/LVVerwaltung/LVVerwaltung.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 7bfb3ec82..faecbf71f 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -262,7 +262,7 @@ export default {
- +
From 0605baf68f5bc9fef210b8827f8b95f8c51707cc Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Wed, 6 Aug 2025 10:45:37 +0200 Subject: [PATCH 061/312] capitalize tab phrases; style&phrase change for Attendance Check Date Datepicker; Picture col min width icrease so the title is readable in english; --- system/phrasesupdate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 034e4aca5..01e4fcdfe 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -31767,7 +31767,7 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'kontrolldatum', + 'phrase' => 'kontrolldatumV2', 'insertvon' => 'system', 'phrases' => array( array( @@ -31778,7 +31778,7 @@ array( ), array( 'sprache' => 'English', - 'text' => 'Attendance Check Date', + 'text' => 'Check Date', 'description' => '', 'insertvon' => 'system' ) From 9ae78f2de673b62e4100b223219304b5a392d636 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 11 Aug 2025 09:23:18 +0200 Subject: [PATCH 062/312] master gemerged --- application/models/person/Notiz_model.php | 2 +- .../js/components/LVVerwaltung/LVVerwaltung.js | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/application/models/person/Notiz_model.php b/application/models/person/Notiz_model.php index b524451cb..bc4494f57 100644 --- a/application/models/person/Notiz_model.php +++ b/application/models/person/Notiz_model.php @@ -198,7 +198,7 @@ class Notiz_model extends DB_Model z.$type = ?"; if ($withoutTags) - $qry .= " AND tbl_notiz.typ IS NULL"; + $qry .= " AND n.typ IS NULL "; $qry .= "GROUP BY notiz_id, z.notizzuordnung_id, diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index faecbf71f..e9d132848 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -119,6 +119,12 @@ export default { activeFilter: this.emp ? 'employee' : this.stg ? 'verband' : null } + if (this.stg !== undefined) + { + this.selectedStudiengang = this.semester !== '' && this.semester + ? `${this.stg}/${this.semester}` + : this.stg; + } this.filter = filter; }, handleRowClicked(data) @@ -164,7 +170,6 @@ export default { params.studiensemester_kurzbz = studiensemester_kurzbz; if (this.filter.emp) params.emp = this.filter.emp; - this.$router.push({ name: routeName, params }); this.selected = []; }, @@ -191,13 +196,6 @@ export default { }, }, created() { - if (this.stg !== undefined) - { - this.selectedStudiengang = this.semester !== '' && this.semester - ? `${this.stg}/${this.semester}` - : this.stg; - } - this.$p.loadCategory(['lehre', 'person', 'global']) this.$api.call(ApiDetails.getStudiensemester()) @@ -261,7 +259,7 @@ export default {
- + From 3c3038362a0db114bd95ea932d703bdacd1fe2d5 Mon Sep 17 00:00:00 2001 From: SimonGschnell Date: Mon, 11 Aug 2025 10:38:26 +0200 Subject: [PATCH 063/312] update(MyLv VueRouter Studiensemester): adds the Studiensemester to the url of the vue router in the mylv view --- application/config/routes.php | 1 + public/js/apps/Dashboard/Fhc.js | 5 +++-- public/js/components/Cis/Mylv/Student.js | 13 ++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/application/config/routes.php b/application/config/routes.php index 360005db7..3cba72628 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -63,6 +63,7 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1'; $route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1'; $route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1'; +$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1'; // Studierendenverwaltung List Routes $route['api/frontend/v1/stv/[sS]tudents/inout'] = 'api/frontend/v1/stv/Students/index'; diff --git a/public/js/apps/Dashboard/Fhc.js b/public/js/apps/Dashboard/Fhc.js index 473e1cf67..da1d8e39f 100644 --- a/public/js/apps/Dashboard/Fhc.js +++ b/public/js/apps/Dashboard/Fhc.js @@ -137,11 +137,12 @@ const router = VueRouter.createRouter({ component: CmsNews, props: true }, + //dafadsdafadsdafadsdafadsdafadsdafadsdafadsdafads { - path: `/Cis/MyLv`, + path: `/Cis/MyLv/:studiensemester?`, name: 'MyLv', component: MylvStudent, - props: true + props: true, }, { path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`, diff --git a/public/js/components/Cis/Mylv/Student.js b/public/js/components/Cis/Mylv/Student.js index 508acb883..e70cd065d 100644 --- a/public/js/components/Cis/Mylv/Student.js +++ b/public/js/components/Cis/Mylv/Student.js @@ -75,17 +75,16 @@ export default { this.$refs.studiensemester.selectedIndex++; this.$refs.studiensemester.dispatchEvent(new Event('change', { bubbles: true })); }, - setHash(val) { - // TODO: make this a router param to enable history - location.hash = val; + updateRouter(val) { + this.$router.push(`/Cis/MyLv/${val}`); } }, created() { axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Studiensemester').then(res => { this.studiensemester = res.data.retval || []; - const hash = location.hash.substring(1); - if (hash && this.studiensemester.filter(s => s.studiensemester_kurzbz == hash).length) - this.currentSemester = hash; + const routerStudiensemester = this.$route.params.studiensemester; + if (routerStudiensemester && this.studiensemester.filter(s => s.studiensemester_kurzbz == routerStudiensemester).length) + this.currentSemester = routerStudiensemester; else this.currentSemester = this.nearestSem; }); @@ -104,7 +103,7 @@ export default { -
-
+
From 645da5a5bf4ed8a195eeb8949c73e1f208c38954 Mon Sep 17 00:00:00 2001 From: SimonGschnell Date: Wed, 13 Aug 2025 12:09:34 +0200 Subject: [PATCH 068/312] fix(Dashboard drag&drop): ensures dragend events trigger correctly in chrome --- public/js/components/Drop/Grid.js | 27 +++++++++++++++------------ public/js/composables/GridLogic.js | 3 ++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/public/js/components/Drop/Grid.js b/public/js/components/Drop/Grid.js index ae1a6504c..cfe5747d4 100644 --- a/public/js/components/Drop/Grid.js +++ b/public/js/components/Drop/Grid.js @@ -434,11 +434,8 @@ export default { }, dragOver(evt) { if ((this.y + 1) > this.rows && (this.mode == MODE_MOVE || this.mode == MODE_RESIZE)) { - this.positionUpdates = this.positionUpdates?.filter(item => { - return item.widgetid == this.draggedItem.data.widgetid; - }) - this.dragEnd(); this.dragCancel(); + } if (!this.active) return this.dragCancel(); @@ -464,7 +461,7 @@ export default { this.draggedOffset[1] += y; y = 0; } - this.positionUpdates = this.dragGrid.move(this.draggedItem, x, y); + this.positionUpdates= this.dragGrid.move(this.draggedItem, x, y); break; } case MODE_RESIZE: { @@ -494,8 +491,11 @@ export default { }, dragEnd() { this.removeWidgetClones(); - if (this.mode == MODE_IDLE) + this.toggleDraggedItemOverlay(false); + + if (this.mode == MODE_IDLE){ return; + } // clean up unused classes let draggedItemNode = document.getElementById(this.draggedItem.data.widgetid); draggedItemNode.classList.remove("border-danger"); @@ -503,16 +503,19 @@ export default { ele.classList.remove("denied-dragging-animation"); }) - - - if (!this.active || this.x < 0 || this.y < 0 || this.x >= this.cols) - return this.dragCancel(); + //if (!this.active || this.x < 0 || this.y < 0 || this.x >= this.cols) + //return this.dragCancel(); + this.mode = MODE_IDLE; let updated = []; this.convertGridResultToUpdate(this.positionUpdates, updated); updated = this._updateFixedPositions(updated); if (updated.length) this.$emit('rearrangeItems', updated.filter(v => v)); + + this.draggedItem = null; + this.draggedNode = null; + this.$emit('draggedItem', null); }, _updateFixedPositions(updated) { updated.forEach((item, index) => { @@ -617,7 +620,7 @@ export default { @touchmove="dragOver" @touchend="dragCancel" @dragover.prevent="dragOver" - @drop="dragEnd" + @drop="dragEnd($event)" @mousemove="updateCursorOnMouseMove" @mouseleave="mouseLeave"> @@ -631,7 +634,7 @@ export default { @mouse-up="mouseUp" @start-resize="startResize" @dragging="dragging" - @end-drag="dragCancel" + @end-drag="dragEnd" @touch-end="dragEnd();mouseUp();" @touch-start="updateCursorOnMouseMove($event); mouseDown();" class="position-absolute" diff --git a/public/js/composables/GridLogic.js b/public/js/composables/GridLogic.js index 93c662eb5..ff8ae0c39 100644 --- a/public/js/composables/GridLogic.js +++ b/public/js/composables/GridLogic.js @@ -116,7 +116,7 @@ class GridLogic { prefer = DIR_RIGHT; } - const originalFrame = [...item.frame]; + const originalFrame = Array.isArray(item.frame) ? [...item.frame] : [item.frame]; const currItem = {...item}; currItem.x = x; @@ -146,6 +146,7 @@ class GridLogic { } } replaceUpdate[item.index] = { index: item.index, x, y }; + return replaceUpdate; } From 34a239a823ffdea8b6dac04b5471d0e0bcb74179 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Wed, 13 Aug 2025 14:54:43 +0200 Subject: [PATCH 069/312] Set Foto to null instead of empty String when Deleted --- application/controllers/api/frontend/v1/stv/Student.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application/controllers/api/frontend/v1/stv/Student.php b/application/controllers/api/frontend/v1/stv/Student.php index ac09f39bb..70970d8b5 100644 --- a/application/controllers/api/frontend/v1/stv/Student.php +++ b/application/controllers/api/frontend/v1/stv/Student.php @@ -276,9 +276,14 @@ class Student extends FHCAPI_Controller $update_person = array(); foreach ($array_allowed_props_person as $prop) { $val = $this->input->post($prop); - if ($val !== null) { + if ($val !== null) + { $update_person[$prop] = $val; } + if($prop == 'foto' && $val == '') + { + $update_person[$prop] = null; + } } $array_allowed_props_student = ['matrikelnr']; From 4d78abf9ec2586e99b02c72f369fabd86814332e Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 14 Aug 2025 13:15:10 +0200 Subject: [PATCH 070/312] Tab Prestudent: use false for boolean and null for other variables for update if field is empty --- .../controllers/api/frontend/v1/stv/Prestudent.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Prestudent.php b/application/controllers/api/frontend/v1/stv/Prestudent.php index 4d0aa5fe1..2a427e569 100644 --- a/application/controllers/api/frontend/v1/stv/Prestudent.php +++ b/application/controllers/api/frontend/v1/stv/Prestudent.php @@ -142,15 +142,15 @@ class Prestudent extends FHCAPI_Controller $update_prestudent[$prop] = $val; } - // allowed to be null, but has to be in postparameter - if ( - in_array($prop, ['zgvdatum', 'zgvmadatum', 'zgvdoktordatum', 'zgv_code', 'zgvmas_code', 'zgvdoktor_code']) - && !isset($update_prestudent[$prop]) - && array_key_exists($prop, $_POST) - ) - { + //set null instead of empty string + if ($val === '' && ($prop != 'dual' || $prop != 'bismelden')){ $update_prestudent[$prop] = null; } + + //set false instead of empty string if boolean + if ($val === '' && ($prop === 'dual' || $prop === 'bismelden')) { + $update_prestudent[$prop] = false; + } } $update_prestudent['updateamum'] = date('c'); From e46f46c95bc41204e44d62010daf23cc10fe957e Mon Sep 17 00:00:00 2001 From: SimonGschnell Date: Thu, 14 Aug 2025 15:07:38 +0200 Subject: [PATCH 071/312] update(Dashboard grid): doesnt allow the user to drag a widget outside of the grid boundaries --- public/js/components/DashboardWidget/Url.js | 2 +- public/js/components/Drop/Grid.js | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/public/js/components/DashboardWidget/Url.js b/public/js/components/DashboardWidget/Url.js index 2763862a9..16eacab90 100644 --- a/public/js/components/DashboardWidget/Url.js +++ b/public/js/components/DashboardWidget/Url.js @@ -159,7 +159,7 @@ export default { // this.$emit('setConfig', true); -> use this to enable widget config mode if needed }, template: /*html*/ ` -
+
diff --git a/public/js/components/Drop/Grid.js b/public/js/components/Drop/Grid.js index cfe5747d4..d11e4c249 100644 --- a/public/js/components/Drop/Grid.js +++ b/public/js/components/Drop/Grid.js @@ -274,8 +274,23 @@ export default { dragging(event){ if(this.mode == MODE_MOVE){ this.toggleDraggedItemOverlay(true); - this.clonedWidget.style.top = `${this.clientY-20}px`; - this.clonedWidget.style.left = `${this.clientX-15}px`; + + const containerRect = this.$refs.container.getBoundingClientRect(); + const clonedWidgetRect = this.clonedWidget.getBoundingClientRect(); + + let desiredTop = this.clientY - 20; + let desiredLeft = this.clientX - 15; + + const minTop = 0; + const maxTop = containerRect.height - clonedWidgetRect.height; + const minLeft = 0; + const maxLeft = containerRect.width - clonedWidgetRect.width; + + const constrainedTop = Math.max(minTop, Math.min(maxTop, desiredTop)); + const constrainedLeft = Math.max(minLeft, Math.min(maxLeft, desiredLeft)); + + this.clonedWidget.style.top = `${constrainedTop}px`; + this.clonedWidget.style.left = `${constrainedLeft}px`; } }, createNewGrid(items) { @@ -413,6 +428,7 @@ export default { setTimeout(() => { this.draggedNode = evt.target.closest(".drop-grid-item"); //clones the widget for the drag Image + let clone = evt.target.closest(".drop-grid-item")?.cloneNode(true); clone.style.zIndex = 5; From 729618cbe7327da8dcc35b00dc04aa3e0cf166e7 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 14 Aug 2025 15:38:03 +0200 Subject: [PATCH 072/312] Tab Status: change format for Date for Check Bismeldestichtag --- application/controllers/api/frontend/v1/stv/Status.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Status.php b/application/controllers/api/frontend/v1/stv/Status.php index 629d5512a..7edb0305c 100644 --- a/application/controllers/api/frontend/v1/stv/Status.php +++ b/application/controllers/api/frontend/v1/stv/Status.php @@ -287,11 +287,11 @@ class Status extends FHCAPI_Controller ]); $this->form_validation->set_rules('_default', '', [ - ['meldestichtag_not_exceeded', function () use ($datum, $isBerechtigtNoStudstatusCheck) { + ['meldestichtag_not_exceeded', function () use ($datum_string, $isBerechtigtNoStudstatusCheck) { if ($isBerechtigtNoStudstatusCheck) return true; // Skip if access right says so - $result = $this->prestudentstatuschecklib->checkIfMeldestichtagErreicht($datum); + $result = $this->prestudentstatuschecklib->checkIfMeldestichtagErreicht($datum_string); return !$this->getDataOrTerminateWithError($result); }], @@ -733,6 +733,7 @@ class Status extends FHCAPI_Controller $result = $this->prestudentstatuschecklib->checkIfMeldestichtagErreicht($oldstatus->datum); + //TODO(Manu) comment out: should not appear during delete (shiva) if (!$this->getDataOrTerminateWithError($result)) $this->terminateWithError( $this->p->t('lehre', 'error_dataVorMeldestichtag'), From a42e70e2fef192977b63926f64975f15070aa308 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 14 Aug 2025 16:28:20 +0200 Subject: [PATCH 073/312] comment out check Bismeldestichtag for deleteStatus --- application/controllers/api/frontend/v1/stv/Status.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Status.php b/application/controllers/api/frontend/v1/stv/Status.php index 7edb0305c..fdf2bc66a 100644 --- a/application/controllers/api/frontend/v1/stv/Status.php +++ b/application/controllers/api/frontend/v1/stv/Status.php @@ -731,15 +731,15 @@ class Status extends FHCAPI_Controller REST_Controller::HTTP_FORBIDDEN ); - $result = $this->prestudentstatuschecklib->checkIfMeldestichtagErreicht($oldstatus->datum); + //TODO(Manu) check this check, meanwhile not active +/* $result = $this->prestudentstatuschecklib->checkIfMeldestichtagErreicht($oldstatus->datum); - //TODO(Manu) comment out: should not appear during delete (shiva) if (!$this->getDataOrTerminateWithError($result)) $this->terminateWithError( $this->p->t('lehre', 'error_dataVorMeldestichtag'), self::ERROR_TYPE_GENERAL, REST_Controller::HTTP_FORBIDDEN - ); + );*/ } // Start DB transaction From f695d8b66a2437420e0e831d0a7d967dd61ba3ce Mon Sep 17 00:00:00 2001 From: SimonGschnell Date: Mon, 18 Aug 2025 10:21:18 +0200 Subject: [PATCH 074/312] update(Dashboard Widget Drag): shows hidden widget when dragging the widget on the dashboard --- public/js/components/Drop/Grid.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/js/components/Drop/Grid.js b/public/js/components/Drop/Grid.js index d11e4c249..a7bea64e3 100644 --- a/public/js/components/Drop/Grid.js +++ b/public/js/components/Drop/Grid.js @@ -434,6 +434,8 @@ export default { clone.style.zIndex = 5; clone.classList.add("widgetClone"); this.$refs.container.appendChild(clone); + const hiddenWidget = clone.querySelector("[style='display: none;']"); + hiddenWidget.style.removeProperty("display"); this.clonedWidget = clone; }, 0); From 42a81a4558e8448b6b752158ad00929b26a4fabe Mon Sep 17 00:00:00 2001 From: chfhtw Date: Mon, 18 Aug 2025 13:06:59 +0200 Subject: [PATCH 075/312] API error clustering --- public/js/plugins/Api.js | 648 ++++++++++++++++++++++++--------------- system/phrasesupdate.php | 20 ++ 2 files changed, 423 insertions(+), 245 deletions(-) diff --git a/public/js/plugins/Api.js b/public/js/plugins/Api.js index 43c2b3064..091a81717 100644 --- a/public/js/plugins/Api.js +++ b/public/js/plugins/Api.js @@ -10,7 +10,232 @@ export default { if (!app.config.globalProperties.$fhcAlert) app.use(FhcAlert); - function _get_config(form, uri, data, config) { + const $fhcAlert = app.config.globalProperties.$fhcAlert; + + function _send_array_or_object(errors, func) { + if (Array.isArray(errors)) + errors.forEach(func); + else + Object.entries(errors).forEach( + ([title, errs]) => errs.forEach( + error => func(error, title) + ) + ); + } + let DEFAULT_ERROR_CONFIG = { + success: true, + fail: true, + combine: { + form: ['validation', 'general'], + toast: ['validation', 'general', 'not_found', 'site_failed'] + }, + handler: { + form(form, errors) { + form.clearValidation(); + errors.forEach(err => form.setFeedback( + false, + err.messages || err.message + )); + }, + async toast(errors) { + const $p = app.config.globalProperties.$p; + if (!$p) + return Promise.reject('Phrasen plugin not loaded!'); + + async function _format_toast(errors) { + errors = errors.reduce((result, err) => { + switch (err.type) { + case 'not_found': + case 'site_failed': + if (err.message) + result[err.message] = [err.url]; + else + result._default = [err.url]; + break; + case 'general': + if (!result._default) + result._default = []; + result._default.push(err.message); + break; + case 'validation': + Object.entries(err.messages) + .forEach(([field, msg]) => { + if (!result[field]) + result[field] = []; + if (Array.isArray(msg)) + result[field].push(...msg); + else + result[field].push(msg); + }); + break; + } + return result; + }, {}); + let counter = 0; + const msgs = await Promise.all(Object.entries(errors) + .sort((a, b) => ['_default'].indexOf(b[0]) - ['_default'].indexOf(a[0])) // sort _default first + .map(async ([field, msgs]) => { + if (field == '_default') { + await $p.loadCategory('dashboard'); + const general = $p.t('dashboard/general'); + field = '
' + general + '
'; + } else { + field = '
' + field + '
'; + } + counter += msgs.length; + return field + + '
' + + msgs.join('
') + + '
'; + })); + return { + counter, + msgs + } + } + + let counter, msgs; + if (Array.isArray(errors)) { + ({ counter, msgs } = await _format_toast(errors)); + } else { + ({ counter, msgs } = await Object.entries(errors) + .reduce(async (res, [title, errs]) => { + const result = await res; + const { counter, msgs } = await _format_toast(errs); + result.counter += counter; + result.msgs.push('
' + + title + + '
' + + msgs.join('') + + '
'); + return result; + }, Promise.resolve({ counter: 0, msgs: []}))); + } + + await $p.loadCategory('ui'); + const n_errors = $p.t('ui/n_errors', { n: counter }); + + $fhcAlert.alertDefault( + 'error', + n_errors, + '
' + msgs.join('') + '
', + true, + true + ); + }, + php(errors) { + _send_array_or_object(errors, (error, title) => { + var message = ''; + message += 'Message: ' + error.message + '\n\n'; + message += 'Filename: ' + error.filename + '\n'; + message += 'Line Number: ' + error.line + '\n'; + if (error.backtrace && error.backtrace.length) { + message += '\nBacktrace: '; + error.backtrace.forEach(err => { + message += '\n\tFile: ' + err.file + '\n'; + message += '\tLine: ' + err.line + '\n'; + message += '\tFunction: ' + err.function + '\n'; + }); + } + switch (error.severity) { + case 'Warning': + case 'Core Warning': + case 'Compile Warning': + case 'User Warning': + if (title) + title += ': PHP ' + error.severity; + else + title = 'PHP ' + error.severity; + $fhcAlert.alertDefault('warn', title, message, true); + break; + case 'Notice': + case 'User Notice': + case 'Runtime Notice': + if (title) + title += ': PHP ' + error.severity; + else + title = 'PHP ' + error.severity; + $fhcAlert.alertDefault('info', title, message, true); + break; + default: + message = 'Type: PHP ' + error.severity + '\n\n' + message; + if (title) + message = title + '\n\n' + message; + $fhcAlert.alertSystemError(message); + break; + } + }); + }, + exception(errors) { + _send_array_or_object(errors, (error, title) => { + var message = ''; + if (title) + message += title + '\n\n'; + message += 'Type: ' + error.class + '\n\n'; + message += 'Message: ' + error.message + '\n\n'; + message += 'Filename: ' + error.filename + '\n'; + message += 'Line Number: ' + error.line + '\n'; + if (error.backtrace && error.backtrace.length) { + message += '\nBacktrace: '; + error.backtrace.forEach(err => { + message += '\n\tFile: ' + err.file + '\n'; + message += '\tLine: ' + err.line + '\n'; + message += '\tFunction: ' + err.function + '\n'; + }); + } + $fhcAlert.alertSystemError(message); + }); + }, + db(errors) { + _send_array_or_object(errors, (error, title) => { + var message = ''; + if (title) + message += title + '\n\n'; + if (error.heading !== undefined) + message += error.heading + '\n\n'; + if (error.code !== undefined) + message += 'Code: ' + error.code + '\n\n'; + if (error.sql !== undefined) + message += 'SQL: ' + error.sql + '\n\n'; + if (error.message !== undefined) + message += 'Message: ' + error.message + '\n\n'; + else if (error.messages !== undefined) + message += 'Messages: ' + error.messages.join('\n\t') + '\n\n'; + if (error.filename !== undefined) + message += 'Filename: ' + error.filename + '\n'; + if (error.line !== undefined) + message += 'Line Number: ' + error.line + '\n'; + + $fhcAlert.alertSystemError(message); + }); + }, + auth(errors) { + _send_array_or_object(errors, (error, title) => { + if (title) + title += ': ' + error.message; + else + title = error.message; + + var message = ''; + message += 'Controller name: ' + error.controller + '\n'; + message += 'Method name: ' + error.method + '\n'; + message += 'Required permissions: ' + error.required_permissions; + + $fhcAlert.alertDefault( + 'error', + title, + message, + true + ); + }); + } + } + }; + + if (options?.errorHandling !== undefined) + DEFAULT_ERROR_CONFIG = _merge_error_config(options.errorHandling); + + function get_config(form, uri, data, config) { if (typeof form == 'string' && config === undefined) { [uri, data, config] = [form, uri, data]; form = undefined; @@ -40,10 +265,9 @@ export default { return [uri, data, config]; } - - function _clean_return_value(response) { + function clean_return_value(response) { if (typeof response.data === 'string' || response.data instanceof String) - return _clean_return_value({ data: response }); + return clean_return_value({ data: response }); const result = response.data; delete response.data; @@ -55,10 +279,111 @@ export default { result.meta.response = response; return result; } - const baseURL = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/"; + function _merge_error_config(config) { + if (config === false || config === 'off') + return { ...DEFAULT_ERROR_CONFIG, success: false, fail: false }; + + if (!config || config === true) + return { ...DEFAULT_ERROR_CONFIG }; + + if (config === 'success') + return { ...DEFAULT_ERROR_CONFIG, fail: false }; + + if (config === 'fail') + return { ...DEFAULT_ERROR_CONFIG, success: false }; + + const { success, fail, handler, combine } = config; + + config = { ...DEFAULT_ERROR_CONFIG }; + + Object.entries({ fail, success }).forEach(([key, value]) => { + if (value !== undefined) + config[key] = value; + }); + Object.entries({ handler, combine }).forEach(([key, value]) => { + if (value !== undefined) + config[key] = { ...config[key], ...value }; + }); + + return config; + } + function get_error_handler(config) { + const result = _merge_error_config(config?.errorHandling); + + if (!config?.form) { + result.combine.form = []; + } else { + const formHandler = result.handler.form; + result.handler.form = errors => formHandler(config.form, errors); + } + + return result; + } + function get_error_list(error) { + if (error.response) { + if (error.response.status == 404) { + return [{ + type: 'not_found', + message: error.message, + url: error.request.responseURL + }]; + } else { + return error.response.data.errors; + } + } else if (error.request) { + return [{ + type: 'site_failed', + message: error.message, + url: error.request.responseURL + }]; + } else { + return [{ + type: 'script', + message: error.message + }]; + } + } + function popHandleableErrors(errorHandling, errors) { + const result = {}; + const copy = []; + while (errors.length) + copy.push(errors.pop()); + for (var error of copy) { + let type = error.type; + let newType = null; + for (var t in errorHandling.combine) { + let newTypeCombinesType = errorHandling + .combine[t] + .includes(type); + let newTypeHasHandler = errorHandling.handler[t]; + if (newTypeCombinesType && newTypeHasHandler) { + newType = t; + if (newType == 'form') + break; + } + } + if (newType) + type = newType; + const handler = errorHandling.handler[type]; + if (handler) { + if (!result[type]) + result[type] = []; + if (Array.isArray(error)) + result[type].push(...error); + else + result[type].push(error); + continue; + } + errors.push(error); + } + return result; + } + const fhcApiAxios = axios.create({ timeout: 500000, - baseURL: FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/" + baseURL: FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + "/" }); fhcApiAxios.interceptors.request.use(config => { @@ -100,57 +425,51 @@ export default { return config; }); - fhcApiAxios.interceptors.response.use(response => { - if (response.config?.errorHandling == 'off' - || response.config?.errorHandling === false - || response.config?.errorHandling == 'fail') - return _clean_return_value(response); - - // NOTE(chris): loop through errors - if (response.data.errors) - response.data.errors = response.data.errors.filter( - err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$api._defaultErrorHandlers[err.type])(err, response.config) - ); - - return _clean_return_value(response); - }, error => { - if (error.code == 'ERR_CANCELED') - return Promise.reject({...{handled: true}, ...error}); - - if (error.config?.errorHandling == 'off' - || error.config?.errorHandling === false - || error.config?.errorHandling == 'success') - return Promise.reject(error); - - if (error.response) { - if (error.response.status == 404) { - app.config.globalProperties.$fhcAlert.alertDefault('error', error.message, error.request.responseURL, true); - return Promise.reject({...{handled: true}, ...error}); - } + fhcApiAxios.interceptors.response.use( + response => { + if (response.config?.errorHandling == 'off' + || response.config?.errorHandling === false + || response.config?.errorHandling == 'fail') + return clean_return_value(response); // NOTE(chris): loop through errors - error.response.data.errors = error.response.data.errors.filter( - err => (error.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$api._defaultErrorHandlers[err.type])(err, error.config) - ); - if (!error.response.data.errors.length) - return Promise.reject({...{handled: true}, ...error}); - } else if (error.request) { - app.config.globalProperties.$fhcAlert.alertDefault('error', error.message, error.request.responseURL); - return Promise.reject({...{handled: true}, ...error}); - } else { - app.config.globalProperties.$fhcAlert.alertError(error.message); - return Promise.reject({...{handled: true}, ...error}); - } + if (response.data.errors) + response.data.errors = response.data.errors.filter( + err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$api._defaultErrorHandlers[err.type])(err, response.config) + ); - return Promise.reject(error); - }); + return clean_return_value(response); + }, + error => { + if (error.code == 'ERR_CANCELED') + return Promise.reject({ handled: true, ...error }); + + const errorConfig = get_error_handler(error.config); + + if (!errorConfig.fail) + return Promise.reject(error); + + const remaining = get_error_list(error); + + const errors = popHandleableErrors(errorConfig, remaining); + + for (var type in errors) { + errorConfig.handler[type](errors[type]); + } + + if (remaining.length) + return Promise.reject(error); + + return Promise.reject({ handled: true, ...error }); + } + ); app.config.globalProperties.$api = { getUri(url) { return fhcApiAxios.getUri({url}); }, get(form, uri, params, config) { - [uri, params, config] = _get_config(form, uri, params, config); + [uri, params, config] = get_config(form, uri, params, config); if (params) { if (config) config.params = params; @@ -160,82 +479,56 @@ export default { return fhcApiAxios.get(uri, config); }, post(form, uri, data, config) { - [uri, data, config] = _get_config(form, uri, data, config); + [uri, data, config] = get_config(form, uri, data, config); return fhcApiAxios.post(uri, data, config); }, call(factory, configoverwrite, form) { if (Array.isArray(factory)) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; const $api = app.config.globalProperties.$api; Promise .allSettled(factory.map((config, index) => { - if (Array.isArray(config)) - return $api.call(config[1], { - errorHeader: config[0], - errorHandling: false - }); - else - return $api.call(config, { - errorHeader: '#' + index, - errorHandling: false - }); - })) - .then(res => { - // TODO(chris): obey form & configoverwrite - let messagesError = []; - let messagesSuccessful = []; - - res.forEach(result => { - if (result.status === 'fulfilled') { - //console.log(JSON.parse(result.value.data)); - const successTitle = "
" + result.value.data + "
"; - messagesSuccessful.push(successTitle + "ok"); - } else { - const errorTitle = "
" + result.reason.config.errorHeader + "
"; - const errorMsg = JSON.parse(result.reason.request.response); - const fullMessage = errorMsg.errors.map(error => { - if (error.type == 'validation') { - // TODO(chris): do we want the keys? - return '
' + Object.values(error.messages).join("
") + '
'; - } - // TODO(chris): other types - if (error.message) - return '
' + error.message + '
'; - if (error.messages) - return '
' + error.messages.join("\n") + '
'; - // TODO(chris): what to do here - return '
' + "Generic Error" + '
'; // TODO(chris): translate - }).join("\n"); - messagesError.push(errorTitle + fullMessage); - } + if (!Array.isArray(config)) + config = ['#' + index, config]; + return $api.call(config[1], { + errorHeader: config[0], + errorHandling: false }); + })) + .then(result => { + const [ , , config ] = get_config(form, undefined, undefined, configoverwrite || {}); + const errorConfig = get_error_handler(config); - if (messagesError.length) - { - const test = document.createElement('b'); - $fhcAlert.alertDefault( - 'error', - messagesError.length + " Fehler", // TODO(chris): translate - '
' + messagesError.join("") + '
', - true, - true - ); + if (!errorConfig.success && !errorConfig.fail) { + return result; } - if (messagesSuccessful.length) - { - const test = document.createElement('b'); - $fhcAlert.alertDefault( - 'info', - 'Feedback', - messagesSuccessful.length + " erfolgreiche Statusänderung(en) durchgeführt", // TODO(chris): translate - false, - true - ); + + const typedErrors = {}; + for (var res of result) { + const [ allowed, item ] = res.status === 'fulfilled' + ? [ errorConfig.success, res.value ] + : [ errorConfig.fail, res.reason ]; + if (!allowed) + return; + + const errors = popHandleableErrors(errorConfig, get_error_list(item)); + + for (var type in errors) { + if (!typedErrors[type]) + typedErrors[type] = { + [item.config.errorHeader]: errors[type] + }; + else + typedErrors[type][item.config.errorHeader] = errors[type]; + } + }; + + for (var errType in typedErrors) { + errorConfig.handler[errType](typedErrors[errType]); } }); } - let {method, url, params, config} = factory; + let { method, url, params, config } = factory; if (configoverwrite !== undefined) { config = configoverwrite; } @@ -251,141 +544,6 @@ export default { } else { console.error("FhcApi: method not allowed:", method); } - }, - _defaultErrorHandlers: { - validation(error, config) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; - - if (config?.form) { - config.form.clearValidation(); - config.form.setFeedback(false, error.messages); - return false; - } - if (Array.isArray(error.messages)) { - error.messages.forEach($fhcAlert.alertError); - return false; - } else if (typeof error.messages == 'object') { - if (config?.errorHeader) - Object.values(error.messages).forEach( - value => $fhcAlert.alertDefault( - 'error', - Array.isArray(config.errorHeader) ? app.config.globalProperties.$p.t.apply(null, config.errorHeader) : config.errorHeader, - value, - true - ) - ); - else - Object.entries(error.messages).forEach( - ([key, value]) => $fhcAlert.alertDefault('error', key, value, true) - ); - return false; - } - return true; - }, - general(error, config) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; - - if (config?.form) - config.form.setFeedback(false, error.message); - else if (config?.errorHeader) - $fhcAlert.alertDefault( - 'error', - Array.isArray(config.errorHeader) ? app.config.globalProperties.$p.t.apply(null, config.errorHeader) : config.errorHeader, - error.message, - true - ); - else - $fhcAlert.alertError(error.message); - }, - php(error) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; - - var message = ''; - message += 'Message: ' + error.message + '\n\n'; - message += 'Filename: ' + error.filename + '\n'; - message += 'Line Number: ' + error.line + '\n'; - if (error.backtrace && error.backtrace.length) { - message += '\nBacktrace: '; - error.backtrace.forEach(err => { - message += '\n\tFile: ' + err.file + '\n'; - message += '\tLine: ' + err.line + '\n'; - message += '\tFunction: ' + err.function + '\n'; - }); - } - switch (error.severity) { - case 'Warning': - case 'Core Warning': - case 'Compile Warning': - case 'User Warning': - $fhcAlert.alertDefault('warn', 'PHP ' + error.severity, message, true); - break; - case 'Notice': - case 'User Notice': - case 'Runtime Notice': - $fhcAlert.alertDefault('info', 'PHP ' + error.severity, message, true); - break; - default: - message = 'Type: PHP ' + error.severity + '\n\n' + message; - $fhcAlert.alertSystemError(message); - break; - } - }, - exception(error) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; - - var message = ''; - message += 'Type: ' + error.class + '\n\n'; - message += 'Message: ' + error.message + '\n\n'; - message += 'Filename: ' + error.filename + '\n'; - message += 'Line Number: ' + error.line + '\n'; - if (error.backtrace && error.backtrace.length) { - message += '\nBacktrace: '; - error.backtrace.forEach(err => { - message += '\n\tFile: ' + err.file + '\n'; - message += '\tLine: ' + err.line + '\n'; - message += '\tFunction: ' + err.function + '\n'; - }); - } - $fhcAlert.alertSystemError(message); - }, - db(error) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; - - var message = ''; - if (error.heading !== undefined) - message += error.heading + '\n\n'; - if (error.code !== undefined) - message += 'Code: ' + error.code + '\n\n'; - if (error.sql !== undefined) - message += 'SQL: ' + error.sql + '\n\n'; - if (error.message !== undefined) - message += 'Message: ' + error.message + '\n\n'; - else if (error.messages !== undefined) - message += 'Messages: ' + error.messages.join('\n\t') + '\n\n'; - if (error.filename !== undefined) - message += 'Filename: ' + error.filename + '\n'; - if (error.line !== undefined) - message += 'Line Number: ' + error.line + '\n'; - - $fhcAlert.alertSystemError(message); - }, - auth(error, config) { - const $fhcAlert = app.config.globalProperties.$fhcAlert; - - var message = ''; - message += 'Controller name: ' + error.controller + '\n'; - message += 'Method name: ' + error.method + '\n'; - message += 'Required permissions: ' + error.required_permissions; - if (config?.errorHeader) - $fhcAlert.alertDefault( - 'error', - Array.isArray(config.errorHeader) ? app.config.globalProperties.$p.t.apply(null, config.errorHeader) : config.errorHeader, - error.message, - true - ); - else - $fhcAlert.alertDefault('error', error.message, message); - } } }; diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 23eb39a21..cb1a8756b 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -1475,6 +1475,26 @@ $phrases = array( ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'n_errors', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '{n} Fehler', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '{n} Error(s)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'ui', From eb8c3213dfb3628ba3a9cbd737ff463e1841b716 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 19 Aug 2025 07:37:33 +0200 Subject: [PATCH 076/312] - ects angabe bei der anmeldung - tablesorter hinzugefuegt - pruefungsort anderer raum moeglich (text) - pruefungsanmeldungen nur noch studiensemester dropdown - kompatible lvs vergleichen --- cis/private/lehre/pruefung/pruefung.js.php | 119 ++- .../pruefung/pruefungsanmeldung.json.php | 78 +- .../lehre/pruefung/pruefungsanmeldung.php | 14 + .../pruefung/pruefungsanmeldungen_liste.php | 10 +- .../pruefungsanmeldungen_liste_ohne_namen.php | 7 + .../pruefungsanmeldungen_verwalten.php | 59 +- config/global.config-default.inc.php | 3 + include/pruefungCis.class.php | 32 + include/pruefungsanmeldung.class.php | 12 +- include/pruefungstermin.class.php | 6 +- locale/de-AT/pruefung.php | 2 + locale/en-US/pruefung.php | 1 + system/dbupdate_3.4.php | 6 +- .../47972_pruefungsverwaltung_ects_angabe.php | 22 + ...hrveranstaltung_kompatibel_vergleichen.php | 682 ++++++++++++++++++ vilesci/lehre/studienordnung.js | 5 +- 16 files changed, 1006 insertions(+), 52 deletions(-) create mode 100644 system/dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php create mode 100644 vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php diff --git a/cis/private/lehre/pruefung/pruefung.js.php b/cis/private/lehre/pruefung/pruefung.js.php index 4142e413c..f2fc2dc7e 100644 --- a/cis/private/lehre/pruefung/pruefung.js.php +++ b/cis/private/lehre/pruefung/pruefung.js.php @@ -382,7 +382,7 @@ function writePruefungsTable(e, data, anmeldung) } else if(new Date() > minimumFrist) { - button = "

"+frist+"'>

"; + button = "

"+frist+"'>

"; } } else @@ -479,13 +479,15 @@ function showPruefungsDetails(prfId, lvId) * @param {type} lvBezeichnung Bezeichnung der Lehrveranstaltung * @param {type} terminVon Beginn der Prüfung * @param {type} terminBis Ende der Prüfung + * @param {type} ects der LV * @returns {undefined} */ -function openDialog(lehrveranstaltung_id, termin_id, lvBezeichnung, terminVon, terminBis) +function openDialog(lehrveranstaltung_id, termin_id, lvBezeichnung, terminVon, terminBis, ects) { $("#lehrveranstaltungHidden").val(lehrveranstaltung_id); $("#terminHidden").val(termin_id); $("#lehrveranstaltung").html(lvBezeichnung); + $("#ectsangabe").val(ects); $.ajax({ dataType: 'json', @@ -582,6 +584,12 @@ function saveAnmeldung(lehrveranstaltung_id, termin_id) if($('#prestudent_studiengang').length) studiengang_kz = $('#prestudent_studiengang option:selected').val(); + var ects = null; + if ($('#ectsangabe').length) + { + ects = $('#ectsangabe').val(); + } + $.ajax({ dataType: 'json', url: "./pruefungsanmeldung.json.php", @@ -593,7 +601,8 @@ function saveAnmeldung(lehrveranstaltung_id, termin_id) bemerkung: bemerkungen, uid: uid, studienverpflichtung_id: studienverpflichtung_id, - studiengang_kz: studiengang_kz + studiengang_kz: studiengang_kz, + ects: ects }, error: loadError, success: function(data){ @@ -804,6 +813,7 @@ function writeAnmeldungen(data, showMessage = true) var pruefung_id = data.result.anmeldungen[0].pruefung_id; var lehrveranstaltung_id = data.result.anmeldungen[0].lehrveranstaltung_id; var ort_kurzbz = data.result.ort_kurzbz; + var anderer_raum = data.result.anderer_raum; var lv_bezeichnung = data.result.lv_bezeichnung; var lv_lehrtyp = data.result.lv_lehrtyp; var prf_termin = data.result.datum; @@ -816,24 +826,33 @@ function writeAnmeldungen(data, showMessage = true) count++; var vorname = d.student.vorname !== "null" ? d.student.vorname : ""; var nachname = d.student.nachname !== "null" ? d.student.nachname : ""; + + let ects = ""; + + ects = d.ects !== null ? "(" + d.ects + " ECTS) ": ""; + + + switch(d.status_kurzbz) { case 'angemeldet': - liste += "
  • "+vorname+" "+nachname+""; + liste += "
  • "+ects+vorname+" "+nachname+""; liste += "
    "+count+"
    ' onclick='anmeldungBestaetigen(\""+d.pruefungsanmeldung_id+"\", \""+terminId+"\", \""+lehrveranstaltung_id+"\");'>"; liste += "
    "; if(d.wuensche !== null) { - liste += ""; + let msg = $('
    ').text(d.wuensche).html(); + liste += `
    `; } liste += "
  • "; break; case 'bestaetigt': - liste += "
  • "+vorname+" "+nachname+""; + liste += "
  • "+ects+vorname+" "+nachname+""; liste += "
    "+count+"
    "; if(d.wuensche !== null) { - liste += ""; + let msg = $('
    ').text(d.wuensche).html(); + liste += `
    `; } break; @@ -848,9 +867,9 @@ function writeAnmeldungen(data, showMessage = true) $("#lvdaten").html(lv_bezeichnung+" ("+prf_termin+")"); $("#anmeldeDaten").html(liste); $("#listeDrucken").html(listenLinks); - if(ort_kurzbz !== null) + if(ort_kurzbz !== null || anderer_raum !== null) { - $("#raumLink").html("t('pruefung/pruefungsraum'); ?>"+ort_kurzbz); + $("#raumLink").html("t('pruefung/pruefungsraum'); ?>"+ (ort_kurzbz ?? anderer_raum)); } else { @@ -883,11 +902,17 @@ function writeAnmeldungen(data, showMessage = true) function openRaumDialog(terminId, lehrveranstaltung_id) { getRaeume(terminId); - $("#raum").html('

    t('pruefung/pruefungsraum'); ?>

    t('pruefung/imBuero'); ?>
    t('pruefung/raum'); ?>: '); + $("#raum").html('

    t('pruefung/pruefungsraum'); ?>

    t('pruefung/imBuero'); ?>
    t('pruefung/raum'); ?>:
    t('pruefung/andererRaum'); ?>
    '); $("#raumSpeichernButton").html("' onclick='saveRaum(\""+terminId+"\", \""+lehrveranstaltung_id+"\");'/>"); $("#raumDialog").dialog("open"); } +function openKommentarDialog(text) +{ + $('#kommentarimDialog').text(text); + $('#kommentarDialog').dialog('open'); +} + /** * speichert die Reihung der Studenten einer Prüfungsanmeldung * @param {type} terminId ID des Prüfungstermines @@ -1118,25 +1143,49 @@ function loadPruefungStudiengang(studiengang_kz, studiensemester) $("#pruefungenListe").empty(); if(data.result.length > 0) { - var liste = ""; - data.result.forEach(function(e){ - liste += "
  • + + + + `; + } + else + { + termine.forEach(function(d) { + let vonDate = convertDateTime(d.von); + let vonTime = convertDateTime(d.von, 'time'); + let bisTime = convertDateTime(d.bis, 'time'); + let onClick = `showAnmeldungen(${d.pruefungstermin_id}, ${e.lehrveranstaltung_id})`; + + rows += ` + + + + + + + + + `; }); } - catch(err) - { - var errmsg = err.message; - } - liste += ""; }); - $("#pruefungenListe").append(liste); + $("#pruefungenListe").html(rows); + setTablesorter('table4') } else { + $('#table4').hide() $("#pruefungenListe").html("t('pruefung/keinePruefungenVorhanden'); ?>"); } } @@ -1241,6 +1290,7 @@ function loadStudiensemester() data.result.forEach(function(d){ selectData += ""; }); + $('#studiensemester').html(selectData); loadPruefungsfenster(); loadLehrveranstaltungen(); @@ -1540,7 +1590,7 @@ function loadPruefungsDetails(prfId) if(data.result.length === 0) { messageBox("message", "t('pruefung/keinePruefungsfensterGespeichert'); ?>", "red", "highlight", 10000); - $("#pruefungsfenster").html(""); + $("#pruefungsfenster").html(""); } else { @@ -2188,10 +2238,28 @@ function changeStateOfRaumDropdown() } } +function changeStateOfRaumInputs() +{ + if ($("#andererRaum").prop("checked") === true) + { + $("#raumInfos").hide(); + } + else + { + $("#raumInfos").show(); + } +} + function saveRaum(terminId, lehrveranstaltung_id) { var ort_kurzbz; - if($("#raum input[type=checkbox]").prop("checked") === true) + let anderer_raum = ''; + if ($("#andererRaum").prop("checked") === true && $('#andereRaumInput').val() !== '') + { + ort_kurzbz = ""; + anderer_raum = $('#andereRaumInput').val(); + } + else if($("#raum input[type=checkbox]").prop("checked") === true) { ort_kurzbz = "buero"; } @@ -2206,7 +2274,8 @@ function saveRaum(terminId, lehrveranstaltung_id) data: { method: "saveRaum", ort_kurzbz: ort_kurzbz, - terminId: terminId + terminId: terminId, + anderer_raum: anderer_raum }, error: loadError }).done(function(data){ diff --git a/cis/private/lehre/pruefung/pruefungsanmeldung.json.php b/cis/private/lehre/pruefung/pruefungsanmeldung.json.php index 6757cf391..54c064d14 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldung.json.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldung.json.php @@ -108,7 +108,7 @@ switch($method) break; case 'getPruefungenStudiengang': $studiensemester = filter_input(INPUT_POST,"studiensemester"); - $data = getPruefungenStudiengang($uid, $studiensemester); + $data = getPruefungenStudiengangBySemester($studiensemester); break; case 'saveKommentar': $data = saveKommentar(); @@ -120,7 +120,8 @@ switch($method) case 'saveRaum': $terminId = $_REQUEST["terminId"]; $ort_kurzbz = $_REQUEST["ort_kurzbz"]; - $data = saveRaum($terminId, $ort_kurzbz, $uid); + $anderer_raum = $_REQUEST["anderer_raum"]; + $data = saveRaum($terminId, $ort_kurzbz, $uid, $anderer_raum); break; case 'getLvKompatibel': $lvid = filter_input(INPUT_POST, "lehrveranstaltung_id"); @@ -397,6 +398,7 @@ function saveAnmeldung($aktStudiensemester = null, $uid = null) $lv_besucht = false; $studienverpflichtung_id = filter_input(INPUT_POST, "studienverpflichtung_id"); $studiengang_kz = filter_input(INPUT_POST, "studiengang_kz"); + $ects = filter_input(INPUT_POST, "ects"); //Defaulteinstellung für Anzahlprüfungsversuche (wird durch Addon "ktu" überschrieben) $maxAnzahlVersuche = 0; @@ -731,6 +733,10 @@ function saveAnmeldung($aktStudiensemester = null, $uid = null) else $anmeldung->anrechnung_id = $anrechnung->anrechnung_id; + if (defined('CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE') && (CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE === true)) + { + $anmeldung->ects = $ects; + } if($anmeldung->save(true)) { $pruefung = new pruefungCis($termin->pruefung_id); @@ -1166,6 +1172,53 @@ function getPruefungenStudiengang($uid, $aktStudiensemester) return $data; } +function getPruefungenStudiengangBySemester($aktStudiensemester) +{ + $result = array(); + $pruefungen = new pruefungCis(); + $pruefungen->getPruefungByStudiensemester($aktStudiensemester); + + if((!empty($pruefungen->lehrveranstaltungen))) + { + $lehrveranstaltungen = []; + foreach ($pruefungen->lehrveranstaltungen as $prf) + { + $pruefung = new pruefungCis(); + $pruefung->load($prf->pruefung_id); + + if ($pruefung->storniert) + continue; + + $pruefung->getTermineByPruefung(); + + $lvid = $prf->lehrveranstaltung_id; + + if (!isset($lehrveranstaltungen[$lvid])) + { + $lv = new stdClass(); + $lehrveranstaltung = new lehrveranstaltung(); + $lehrveranstaltung->load($lvid); + + $studiengang = new studiengang(); + $studiengang->load($lehrveranstaltung->studiengang_kz); + + $lv->bezeichnung = $lehrveranstaltung->bezeichnung; + $lv->lehrveranstaltung_id = $lvid; + $lv->studiengang = $studiengang->kuerzel; + $lv->pruefung = []; + $lehrveranstaltungen[$lvid] = $lv; + } + + $lehrveranstaltungen[$lvid]->pruefung[] = $pruefung; + } + $result = array_values($lehrveranstaltungen); + } + $data['result']=$result; + $data['error']='false'; + $data['errormsg']=''; + return $data; +} + /** * * @return typespeichert ein Kommentar zu einer Prüfungsanmeldung @@ -1246,7 +1299,7 @@ function compareRaeume($a, $b) return strcmp($a->ort_kurzbz, $b->ort_kurzbz); } -function saveRaum($terminId, $ort_kurzbz, $uid) +function saveRaum($terminId, $ort_kurzbz, $uid, $anderer_raum = '') { $terminkollision = defined('CIS_PRUEFUNGSANMELDUNG_ERLAUBE_TERMINKOLLISION') ? CIS_PRUEFUNGSANMELDUNG_ERLAUBE_TERMINKOLLISION : false; $pruefungstermin = new pruefungstermin($terminId); @@ -1265,7 +1318,24 @@ function saveRaum($terminId, $ort_kurzbz, $uid) { $pruefung = new pruefungCis($pruefungstermin->pruefung_id); $mitarbeiter = new mitarbeiter($pruefung->mitarbeiter_uid); - if($ort_kurzbz === "buero") + + if ($ort_kurzbz === "" && $anderer_raum !== "") + { + $pruefungstermin->anderer_raum = $anderer_raum; + + if($pruefungstermin->save(false)) + { + $data['result']="reserviert"; + $data['error']='false'; + $data['errormsg']=''; + } + else + { + $data['error']='true'; + $data['errormsg']=$pruefungstermin->errormsg; + } + } + else if($ort_kurzbz === "buero") { $pruefungstermin->ort_kurzbz = $mitarbeiter->ort_kurzbz; if($pruefungstermin->save(false)) diff --git a/cis/private/lehre/pruefung/pruefungsanmeldung.php b/cis/private/lehre/pruefung/pruefungsanmeldung.php index c969c51aa..837e0c227 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldung.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldung.php @@ -321,6 +321,20 @@ $studiensemester->getAll(); + + + + + + + diff --git a/cis/private/lehre/pruefung/pruefungsanmeldungen_liste.php b/cis/private/lehre/pruefung/pruefungsanmeldungen_liste.php index 39ec68ad4..91c2ea084 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldungen_liste.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldungen_liste.php @@ -235,12 +235,18 @@ $rechte->getBerechtigungen($uid); + + + + + uid); @@ -269,6 +275,8 @@ $rechte->getBerechtigungen($uid); echo ''; echo ''; echo ''; + if (defined('CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE') && (CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE === true)) + echo ''; echo ''; } ?> diff --git a/cis/private/lehre/pruefung/pruefungsanmeldungen_liste_ohne_namen.php b/cis/private/lehre/pruefung/pruefungsanmeldungen_liste_ohne_namen.php index 207ba4dd7..414ebdf15 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldungen_liste_ohne_namen.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldungen_liste_ohne_namen.php @@ -233,6 +233,11 @@ $rechte->getBerechtigungen($uid); + + + @@ -265,6 +270,8 @@ $rechte->getBerechtigungen($uid); echo ''; echo ''; echo ''; + if (defined('CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE') && (CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE === true)) + echo ''; echo ''; } ?> diff --git a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php index 22bca21cc..dd38950b1 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php @@ -95,7 +95,7 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung #prfWrapper { position: absolute; height: 70%; - width: 300px; + width: 40%; top: 180px; padding: 1.8em 1.5em 1.8em 1em; /*border-radius: 25px;*/ @@ -117,9 +117,9 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung #anmWrapper { position: absolute; /*top: 45px;*/ - left: 350px; + left: 45%; top: 180px; - width: 500px; + width: 40%; height: 70%; padding: 1.8em 1.5em 1.8em 1em; /*border-radius: 25px;*/ @@ -228,7 +228,6 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung

    t('pruefung/anmeldungenVerwalten'); ?>

    -
    -
    -

    t('global/studiengang'); ?>

    -
    -
    -

    t('global/studiensemester'); ?>

    result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung $studiensemester->getPlusMinus(null, 5); foreach($studiensemester->studiensemester as $sem) { - /*@var $sem studiensemester */ if ($aktuellesSemester == $sem->studiensemester_kurzbz) { echo ''; @@ -273,9 +290,20 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung

    t('pruefung/pruefungPruefungenTitle'); ?>

    -
      - -
    +
    NachnameVornameGebDatumGeschlechtAdresseStatusDetails
    '."$row->nachname$row->vorname$row->gebdatum$row->svnr".($row->geschlecht=='m'?'männlich':'weiblich').""; + echo '
    '."$row->nachname$row->vorname$row->gebdatum".((strpos($status, 'Mitarbeiter') !== false) ? $row->svnr : '')."".($row->geschlecht=='m'?'männlich':'weiblich').""; $qry_adr = "SELECT * FROM public.tbl_adresse WHERE person_id='$row->person_id'"; if($result_adr = $db->db_query($qry_adr)) while($row_adr=$db->db_fetch_object($result_adr)) diff --git a/vilesci/personen/preinteressent_detail.php b/vilesci/personen/preinteressent_detail.php index 5f86e7369..a49c1a0c6 100644 --- a/vilesci/personen/preinteressent_detail.php +++ b/vilesci/personen/preinteressent_detail.php @@ -338,7 +338,6 @@ if(isset($_POST['saveperson'])) $person->gebzeit = $_POST['gebzeit']; $person->anmerkungen = $_POST['anmerkungen']; $person->homepage = $_POST['homepage']; - $person->svnr = $_POST['svnr']; $person->ersatzkennzeichen = $_POST['ersatzkennzeichen']; $person->familienstand = $_POST['familienstand']; $person->geschlecht = $_POST['geschlecht']; @@ -606,8 +605,6 @@ if($result = $db->db_query($qry)) } echo '
    SVNR:Ersatzkennzeichen
    ${e.bezeichnung}Keine Termine
    ${e.studiengang}${e.bezeichnung}${vonDate}${vonTime}${bisTime}t('pruefung/pruefungsbewertungAnmeldungen'); ?>
    + t('pruefung/ects'); ?>: + + +
    t('global/datum'); ?> t('benotungstool/note'); ?> t('global/anmerkung'); ?>t('pruefung/ects'); ?>
    '.$date.''. $anmeldung->ects .'
    t('global/datum'); ?> t('benotungstool/note'); ?> t('global/anmerkung'); ?>t('pruefung/ects'); ?>
    '.$date.''. $anmeldung->ects .'
    + + + + + + + + + + + + +
    @@ -311,6 +339,9 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung
    +
    diff --git a/config/global.config-default.inc.php b/config/global.config-default.inc.php index 8cfc36bfb..89b02a8ce 100644 --- a/config/global.config-default.inc.php +++ b/config/global.config-default.inc.php @@ -263,6 +263,9 @@ define('CIS_SAALPLAN_ALLERAEUME_OPTION', false); //Legt fest ob Bestätigungsmails über eine Anmelung zu einer Prüfung an eine einzelne Person erfolgt oder an den jeweiligen Lektor. (Leerstring für jeweiligen Lektor | uid); define('CIS_PRUEFUNG_MAIL_EMPFAENGER_ANMEDLUNG',""); +//Gibt an, ob die Studierenden bei der Anmeldung zu einer Prüfung den Ausmas an ECTS angeben können +define('CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE', false); + // Username fuer STIP Schnittstelle define('STIP_USER_NAME','stipendienstelle'); // Passwort fuer STIP Schnittstelle diff --git a/include/pruefungCis.class.php b/include/pruefungCis.class.php index ec402ed5d..29b0c425d 100644 --- a/include/pruefungCis.class.php +++ b/include/pruefungCis.class.php @@ -464,6 +464,7 @@ class pruefungCis extends basis_db $obj->anmeldung_von= $row->anmeldung_von; $obj->anmeldung_bis = $row->anmeldung_bis; $obj->ort_kurzbz = $row->ort_kurzbz; + $obj->anderer_raum = $row->anderer_raum; $obj->sammelklausur = $row->sammelklausur; array_push($this->termine, $obj); } @@ -646,6 +647,37 @@ class pruefungCis extends basis_db return false; } + public function getPruefungByStudiensemester($studiensemester_kurzbz) + { + if(empty($studiensemester_kurzbz)) + { + $this->errormsg = "Kein Studiensemester übergeben.
    "; + return false; + } + + $qry = 'SELECT * + FROM campus.tbl_lehrveranstaltung_pruefung + JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id) + JOIN campus.tbl_pruefung USING (pruefung_id) + WHERE tbl_pruefung.studiensemester_kurzbz = ' . $this->db_add_param($studiensemester_kurzbz); + + if($this->db_query($qry)) + { + while($row = $this->db_fetch_object()) + { + $obj = new stdClass(); + $obj->lehrveranstaltung_pruefung_id = $row->lehrveranstaltung_pruefung_id; + $obj->lehrveranstaltung_id = $row->lehrveranstaltung_id; + $obj->pruefung_id = $row->pruefung_id; + $obj->storniert = $row->storniert; + $obj->bezeichnung = $row->bezeichnung; + array_push($this->lehrveranstaltungen, $obj); + } + return true; + } + return false; + } + /** * Lädt alle Prüfung-Lehrveranstaltung Kombinationen * @return boolean true, wenn ok; false, im Fehlerfall diff --git a/include/pruefungsanmeldung.class.php b/include/pruefungsanmeldung.class.php index e4fbec506..c23b0212b 100644 --- a/include/pruefungsanmeldung.class.php +++ b/include/pruefungsanmeldung.class.php @@ -39,6 +39,7 @@ class pruefungsanmeldung extends basis_db { public $statusupdatevon; //varchar(32) public $statusupdateamum; //timestamp public $anrechnung_id; //integer + public $ects; //integer public $pruefungstyp_kurzbz; //varchar(32) public $insertamum; // timestamp @@ -94,7 +95,7 @@ class pruefungsanmeldung extends basis_db { if($new) { - $qry = 'INSERT INTO campus.tbl_pruefungsanmeldung (uid, pruefungstermin_id, lehrveranstaltung_id, status_kurzbz, wuensche, reihung, kommentar, anrechnung_id, pruefungstyp_kurzbz) VALUES (' + $qry = 'INSERT INTO campus.tbl_pruefungsanmeldung (uid, pruefungstermin_id, lehrveranstaltung_id, status_kurzbz, wuensche, reihung, kommentar, anrechnung_id, pruefungstyp_kurzbz, ects) VALUES (' . $this->db_add_param($this->uid).', ' . $this->db_add_param($this->pruefungstermin_id).', ' . $this->db_add_param($this->lehrveranstaltung_id).', ' @@ -103,7 +104,8 @@ class pruefungsanmeldung extends basis_db { . $this->db_add_param($this->reihung).', ' . $this->db_add_param($this->kommentar).', ' . $this->db_add_param($this->anrechnung_id).', ' - . $this->db_add_param($this->pruefungstyp_kurzbz).');'; + . $this->db_add_param($this->pruefungstyp_kurzbz).', ' + . $this->db_add_param($this->ects).');'; } else { @@ -116,7 +118,8 @@ class pruefungsanmeldung extends basis_db { . 'reihung='.$this->db_add_param($this->reihung).', ' . 'kommentar='.$this->db_add_param($this->kommentar).', ' . 'anrechnung_id='.$this->db_add_param($this->anrechnung_id).', ' - . 'pruefungstyp_kurzbz='.$this->db_add_param($this->pruefungstyp_kurzbz) + . 'pruefungstyp_kurzbz='.$this->db_add_param($this->pruefungstyp_kurzbz).', ' + . 'ects='.$this->db_add_param($this->ects) . ' WHERE pruefungsanmeldung_id='.$this->db_add_param($this->pruefungsanmeldung_id).';'; } @@ -167,6 +170,7 @@ class pruefungsanmeldung extends basis_db { $this->statusupdatevon = $row->statusupdatevon; $this->anrechnung_id = $row->anrechnung_id; $this->pruefungstyp_kurzbz = $row->pruefungstyp_kurzbz; + $this->ects = $row->ects; $this->insertamum = $row->insertamum; } return true; @@ -221,6 +225,7 @@ class pruefungsanmeldung extends basis_db { $anmeldung->statusupdatevon = $row->statusupdatevon; $anmeldung->anrechnung_id = $row->anrechnung_id; $anmeldung->pruefungstyp_kurzbz = $row->pruefungstyp_kurzbz; + $anmeldung->ects = $row->ects; array_push($anmeldungen, $anmeldung); } return $anmeldungen; @@ -280,6 +285,7 @@ class pruefungsanmeldung extends basis_db { $anmeldung->statusupdatevon = $row->statusupdatevon; $anmeldung->anrechnung_id = $row->anrechnung_id; $anmeldung->pruefungstyp_kurzbz = $row->pruefungstyp_kurzbz; + $anmeldung->ects = $row->ects; $anmeldung->datum_anmeldung = $row->datum_anmeldung; array_push($anmeldungen, $anmeldung); } diff --git a/include/pruefungstermin.class.php b/include/pruefungstermin.class.php index 74222cceb..c7eb0b085 100644 --- a/include/pruefungstermin.class.php +++ b/include/pruefungstermin.class.php @@ -37,6 +37,7 @@ class pruefungstermin extends basis_db{ public $anmeldung_von; //date public $anmeldung_bis; //date public $ort_kurzbz; //varchar(16) + public $anderer_raum; //text public $sammelklausur; //boolean /** @@ -93,6 +94,7 @@ class pruefungstermin extends basis_db{ $this->anmeldung_von= $row->anmeldung_von; $this->anmeldung_bis = $row->anmeldung_bis; $this->ort_kurzbz = $row->ort_kurzbz; + $this->anderer_raum = $row->anderer_raum; $this->sammelklausur = $row->sammelklausur; } return true; @@ -179,7 +181,8 @@ class pruefungstermin extends basis_db{ . 'teilnehmer_min='.$this->db_add_param($this->teilnehmer_min).', ' . 'anmeldung_von='.$this->db_add_param($this->anmeldung_von).', ' . 'anmeldung_bis='.$this->db_add_param($this->anmeldung_bis).', ' - . 'ort_kurzbz='.$this->db_add_param($this->ort_kurzbz).' ' + . 'ort_kurzbz='.$this->db_add_param($this->ort_kurzbz).', ' + . 'anderer_raum='.$this->db_add_param($this->anderer_raum).' ' . 'WHERE pruefungstermin_id='.$this->db_add_param($this->pruefungstermin_id).';'; } @@ -228,6 +231,7 @@ class pruefungstermin extends basis_db{ $obj->anmeldung_von= $row->anmeldung_von; $obj->anmeldung_bis = $row->anmeldung_bis; $obj->ort_kurzbz = $row->ort_kurzbz; + $obj->anderer_raum = $row->anderer_raum; $obj->sammelklausur = $row->sammelklausur; array_push($this->result, $obj); } diff --git a/locale/de-AT/pruefung.php b/locale/de-AT/pruefung.php index 93b7039e0..9e75fa89e 100644 --- a/locale/de-AT/pruefung.php +++ b/locale/de-AT/pruefung.php @@ -57,6 +57,7 @@ $this->phrasen['pruefung/listeOhneNamenDrucken'] = 'Liste ohne Namen drucken'; $this->phrasen['pruefung/pruefungsraum'] = 'Prüfungsraum: '; $this->phrasen['pruefung/pruefungsort'] = 'Prüfungsort '; $this->phrasen['pruefung/imBuero'] = ' im Büro'; +$this->phrasen['pruefung/andererRaum'] = 'anderer Raum'; $this->phrasen['pruefung/raum'] = ' Raum'; $this->phrasen['pruefung/raumSpeichern'] = ' Raum speichern'; $this->phrasen['pruefung/reihunghErfolgreichGeaendert'] = 'Reihung erfolgreich geändert.'; @@ -95,6 +96,7 @@ $this->phrasen['pruefung/lvVonStudiengang'] = 'Lehrveranstaltungen von Studienga $this->phrasen['pruefung/lvAlle'] = 'Alle Lehrveranstaltungen'; $this->phrasen['pruefung/anmeldungSpeichern'] = 'Anmeldung speichern'; $this->phrasen['pruefung/studienverpflichtung'] = 'Studienverpflichtung'; +$this->phrasen['pruefung/ects'] = 'ECTS'; // liste $this->phrasen['pruefung/anmeldungsliste'] = 'Anmeldungsliste'; diff --git a/locale/en-US/pruefung.php b/locale/en-US/pruefung.php index cd5175c13..7b0cec97e 100644 --- a/locale/en-US/pruefung.php +++ b/locale/en-US/pruefung.php @@ -57,6 +57,7 @@ $this->phrasen['pruefung/listeOhneNamenDrucken'] = 'Liste ohne Namen drucken'; $this->phrasen['pruefung/pruefungsraum'] = 'Prüfungsraum: '; $this->phrasen['pruefung/pruefungsort'] = 'Prüfungsort '; $this->phrasen['pruefung/imBuero'] = ' im Büro'; +$this->phrasen['pruefung/andererRaum'] = 'other Room'; $this->phrasen['pruefung/raum'] = ' Raum'; $this->phrasen['pruefung/raumSpeichern'] = ' Raum speichern'; $this->phrasen['pruefung/reihunghErfolgreichGeaendert'] = 'Reihung erfolgreich geändert.'; diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index cf2f40ca8..3a743f065 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -77,6 +77,8 @@ require_once('dbupdate_3.4/55614_perm_verwaltetoe.php'); require_once('dbupdate_3.4/25999_C4_dashboard.php'); require_once('dbupdate_3.4/61730_Dashboard_Anpassungen.php'); require_once('dbupdate_3.4/40128_search.php'); +require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php'); +require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

    Pruefe Tabellen und Attribute!

    '; @@ -159,8 +161,8 @@ $tabellen=array( "campus.tbl_paabgabe" => array("paabgabe_id","projektarbeit_id","paabgabetyp_kurzbz","fixtermin","datum","kurzbz","abgabedatum", "insertvon","insertamum","updatevon","updateamum"), "campus.tbl_pruefungsfenster" => array("pruefungsfenster_id","studiensemester_kurzbz","oe_kurzbz","start","ende"), "campus.tbl_pruefung" => array("pruefung_id","mitarbeiter_uid","studiensemester_kurzbz","pruefungsfenster_id","pruefungstyp_kurzbz","titel","beschreibung","methode","einzeln","storniert","insertvon","insertamum","updatevon","updateamum","pruefungsintervall"), - "campus.tbl_pruefungstermin" => array("pruefungstermin_id","pruefung_id","von","bis","teilnehmer_max","teilnehmer_min","anmeldung_von","anmeldung_bis","ort_kurzbz","sammelklausur"), - "campus.tbl_pruefungsanmeldung" => array("pruefungsanmeldung_id","uid","pruefungstermin_id","lehrveranstaltung_id","status_kurzbz","wuensche","reihung","kommentar","statusupdatevon","statusupdateamum","anrechnung_id","pruefungstyp_kurzbz","insertamum"), + "campus.tbl_pruefungstermin" => array("pruefungstermin_id","pruefung_id","von","bis","teilnehmer_max","teilnehmer_min","anmeldung_von","anmeldung_bis","ort_kurzbz","sammelklausur", "anderer_raum"), + "campus.tbl_pruefungsanmeldung" => array("pruefungsanmeldung_id","uid","pruefungstermin_id","lehrveranstaltung_id","status_kurzbz","wuensche","reihung","kommentar","statusupdatevon","statusupdateamum","anrechnung_id","pruefungstyp_kurzbz","insertamum", "ects"), "campus.tbl_pruefungsstatus" => array("status_kurzbz","bezeichnung"), "campus.tbl_reservierung" => array("reservierung_id","ort_kurzbz","studiengang_kz","uid","stunde","datum","titel","beschreibung","semester","verband","gruppe","gruppe_kurzbz","veranstaltung_id","insertamum","insertvon"), "campus.tbl_studierendenantrag" => array("studierendenantrag_id","prestudent_id","studiensemester_kurzbz","datum","typ","insertamum","insertvon","datum_wiedereinstieg","grund","dms_id"), diff --git a/system/dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php b/system/dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php new file mode 100644 index 000000000..7b961fa3d --- /dev/null +++ b/system/dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php @@ -0,0 +1,22 @@ +db_query("SELECT ects FROM campus.tbl_pruefungsanmeldung LIMIT 1")) +{ + $qry = 'ALTER TABLE campus.tbl_pruefungsanmeldung ADD COLUMN ects numeric(5,2);'; + + if(!$db->db_query($qry)) + echo ' campus.tbl_pruefungsanmeldung '.$db->db_last_error().'
    '; + else + echo '
    campus.tbl_pruefungsanmeldung: Neue Spalte ects hinzugefügt'; +} + +if(!@$db->db_query("SELECT anderer_raum FROM campus.tbl_pruefungstermin LIMIT 1")) +{ + $qry = 'ALTER TABLE campus.tbl_pruefungstermin ADD COLUMN anderer_raum text NULL;'; + + if(!$db->db_query($qry)) + echo ' campus.tbl_pruefungstermin '.$db->db_last_error().'
    '; + else + echo '
    campus.tbl_pruefungstermin: Neue Spalte anderer_raum hinzugefügt'; +} diff --git a/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php b/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php new file mode 100644 index 000000000..94e1af155 --- /dev/null +++ b/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php @@ -0,0 +1,682 @@ +load($_POST['lv_id']); + + $kompatibleLvs = $lv->loadLVkompatibel($_POST['lv_id']); + if (is_array($kompatibleLvs) && count($kompatibleLvs) > 0) + { + $result = array(); + foreach($kompatibleLvs as $lvId) + { + $lv->load($lvId); + $studiengang = new studiengang(); + $studiengang->load($lv->studiengang_kz); + $oe = new organisationseinheit(); + $oe->load($lv->oe_kurzbz); + + + $result[] = array( + "lehrveranstaltung_id" => $lv->lehrveranstaltung_id, + "kurzbz" => $lv->kurzbz, + "bezeichnung" => $lv->bezeichnung, + "ects" => $lv->ects, + "studiengang_kuerzel" => $studiengang->kuerzel, + "oe_bezeichnung" => $oe->bezeichnung + ); + } + echo json_encode(["result" => $result]); + exit(); + } + echo json_encode(["result" => array()]); + exit(); + } +} + +if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || $_REQUEST['autocomplete'] === 'To')) +{ + $search = trim((isset($_REQUEST['term']) ? $_REQUEST['term'] : '')); + if (is_null($search) || $search == '') + { + exit(); + } + + $qry = "SELECT lehre.tbl_lehrveranstaltung.* + FROM lehre.tbl_lehrveranstaltung + WHERE + lower(bezeichnung) like '%" . $db->db_escape(mb_strtolower($search)) . "%' OR + lehrveranstaltung_id::text like '%" . $db->db_escape(mb_strtolower($search)) . "%' OR + studiengang_kz::text like '%" . $db->db_escape(mb_strtolower($search)) . "%' + ORDER BY lehrveranstaltung_id DESC + LIMIT 10 + "; + + if ($result = $db->db_query($qry)) + { + $result_obj = array(); + while ($row = $db->db_fetch_object($result)) + { + $item['lehrveranstaltung_id'] = html_entity_decode($row->lehrveranstaltung_id); + $item['bezeichnung'] = html_entity_decode($row->bezeichnung); + $item['oe_kurzbz'] = html_entity_decode($row->oe_kurzbz); + $result_obj[] = $item; + } + echo json_encode($result_obj); + } + exit(); +} +?> + + + Lehrveranstaltung Verwaltung + + + + + + + + + + + + + + +getBerechtigungen($user); + +if(!$rechte->isBerechtigt('lehre/lehrveranstaltung', 'suid')) + die('Sie haben keine Berechtigung fuer diese Seite'); + +$studiengang = new studiengang(); +$studiengang->getAll("kurzbzlang"); + +$organisationseinheit = new organisationseinheit(); +$organisationseinheit->getAll(true, true); + +echo "
    +
    +
    + + + + + + + + + "; + echo " + + + + + + + + + + +
    Suche: + +
    Studiengang: + + OE: +
    Semester: + +
    Lehrveranstaltungen: + +
    +
    + Kompatible Lehrveranstaltungen - + + + + + + + + + + + + + + + + +
    IDKurzbezeichnungBezeichnungECTSStudiengangOrganisationseiheitÜbernehmen?Löschen?
    + + + +
    +
    +
    + + + + + + + + + + "; + echo " + + + + + + + + + + +
    Suche: + +
    Studiengang: + + OE: +
    Semester: + +
    Lehrveranstaltungen: + +
    +
    + Kompatible Lehrveranstaltungen - + + + + + + + + + + + + + + +
    IDKurzbezeichnungBezeichnungECTSStudiengangOrganisationseiheitLöschen?
    +
    +"; + + +echo " + "; + +?> diff --git a/vilesci/lehre/studienordnung.js b/vilesci/lehre/studienordnung.js index 54fa9e81b..bf7ff34e7 100644 --- a/vilesci/lehre/studienordnung.js +++ b/vilesci/lehre/studienordnung.js @@ -695,15 +695,16 @@ function loadLVKompatibilitaet(lvid) }).success(function(data) { var html=''; + html = html+'kompatible Lehrveranstaltungen vergleichen

    '; + html = html+'kompatible Lehrveranstaltungen hinzufügen
    '; for(i in data.result) { if(data.result[i]) { lvdata = data.result[i]; if(!(lvdata.kurzbz===null && lvdata.bezeichnung===null && lvdata.studiengang_kz===null && lvdata.semester===null)) - html = html+'
    '+lvdata.kurzbz+' - '+lvdata.bezeichnung+' (Studiengang '+lvdata.studiengang_kz+', Semester '+lvdata.semester+')'; + html = html+'
    '+lvdata.kurzbz+' - '+lvdata.bezeichnung+' (Studiengang '+lvdata.studiengang_kz+', Semester '+lvdata.semester+')
    '; } - html = html+'

    kompatible Lehrveranstaltungen hinzufügen'; } $("#tab-kompatibel").html(html); From c5001f87bda7556c148332babd1efdb9647f5a05 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Tue, 19 Aug 2025 11:03:23 +0200 Subject: [PATCH 077/312] modal header flex container for buttons (close, expand); --- public/js/components/Bootstrap/Modal.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/public/js/components/Bootstrap/Modal.js b/public/js/components/Bootstrap/Modal.js index 4487127de..61c5c6857 100644 --- a/public/js/components/Bootstrap/Modal.js +++ b/public/js/components/Bootstrap/Modal.js @@ -135,12 +135,13 @@ export default {
    ${e.studiengang} ${e.bezeichnung} ${vonDate}
    + + + + + + "; + + foreach($mail_benutzer as $benutzer) + { + $html .= " + + "; + } + $html .= "
    UID
    " . htmlspecialchars($benutzer['uid']) . "

    "; + + $mail = new mail($email, $from, $subject, $p->t('pruefung/emailBodyBitteHtmlSicht')); + $mail->setHTMLContent($html); + $mail->send(); + } + } $data['result']=true; $data['error']='false'; $data['errormsg']=''; @@ -1038,6 +1098,12 @@ function anmeldungBestaetigen($uid) $ort = new ort($termin->ort_kurzbz); $pruefung = new pruefungCis($termin->pruefung_id); + $ortbezeichnung = $ort->bezeichnung; + if (is_null($termin->ort_kurzbz) && !is_null($termin->anderer_raum)) + { + $ortbezeichnung = $termin->anderer_raum; + } + $to = $anmeldung->uid."@".DOMAIN; $from = "noreply@".DOMAIN; $subject = $p->t('pruefung/emailSubjectAnmeldungBestaetigung'); @@ -1055,7 +1121,7 @@ function anmeldungBestaetigen($uid) } else $html .= $p->t('pruefung/emailBodyTermin')." ".$datum->formatDatum($termin->von, "d.m.Y")." ".$p->t('pruefung/emailBodyUm')." ".$datum->formatDatum($termin->von, "H:i")."
    "; - $html .= $p->t('pruefung/anmeldungErfolgreich')." ".$ort->bezeichnung."
    "; + $html .= $p->t('pruefung/anmeldungErfolgreich')." ".$ortbezeichnung."
    "; $html .= "
    "; $html .= "".$p->t('pruefung/emailBodyLinkZurAnmeldung')."
    "; $html .= "
    "; @@ -1178,7 +1244,7 @@ function getPruefungenStudiengangBySemester($aktStudiensemester) $pruefungen = new pruefungCis(); $pruefungen->getPruefungByStudiensemester($aktStudiensemester); - if((!empty($pruefungen->lehrveranstaltungen))) + if(!empty($pruefungen->lehrveranstaltungen)) { $lehrveranstaltungen = []; foreach ($pruefungen->lehrveranstaltungen as $prf) diff --git a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php index dd38950b1..653d29c02 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php @@ -247,18 +247,16 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung } }); - $("#table4").tablesorter( - { - - widgets: ["zebra"], - headers: { - 0: { sorter: false }, - 3: { sorter: 'shortDate', - dateFormat: 'ddmmyyyy' }, - 4: { sorter: 'time' }, - 5: { sorter: 'time' } - } - }); + $("#table4").tablesorter({ + widgets: ["zebra"], + headers: { + 0: { sorter: false }, + 3: { sorter: 'shortDate', + dateFormat: 'ddmmyyyy' }, + 4: { sorter: 'time' }, + 5: { sorter: 'time' } + } + }); }); diff --git a/locale/de-AT/pruefung.php b/locale/de-AT/pruefung.php index 9e75fa89e..fbfb66b8f 100644 --- a/locale/de-AT/pruefung.php +++ b/locale/de-AT/pruefung.php @@ -78,6 +78,7 @@ $this->phrasen['pruefung/unbegrenzt'] = 'unbegrenzt'; $this->phrasen['pruefung/bewertungenZu'] = 'Bewertungen zu'; $this->phrasen['pruefung/keineAuswahl'] = 'Keine Auswahl'; $this->phrasen['pruefung/alleBestaetigen'] = 'Alle bestätigen'; +$this->phrasen['pruefung/bitteEmailAngeben'] = 'Bitte geben Sie eine gültige E-Mail-Adresse ein.'; // pruefungsbewertung $this->phrasen['pruefung/pruefungsbewertungTitle'] = 'Prüfungsbewertung'; @@ -141,6 +142,15 @@ $this->phrasen['pruefung/emailBodyOrt'] = 'Ort:'; $this->phrasen['pruefung/emailBodyLinkZurAnmeldung'] = 'Link zur Anmeldung'; $this->phrasen['pruefung/emailBodyBitteHtmlSicht'] = 'Bitte sehen Sie sich die Nachricht in HTML Sicht an, um den Link vollständig darzustellen.'; + +// Sammelmail +$this->phrasen['pruefung/sammelemailBody'] = 'Die Anmeldungen zur Prüfung %s am %s wurde von %s bestätigt.
    '; +$this->phrasen['pruefung/sammelemailBody2'] = 'Ort: %s
    '; +$this->phrasen['pruefung/sammelemailBodyBestaetigt'] = 'Bestätigt von'; +$this->phrasen['pruefung/sammelemailBodyPruefung'] = 'Prüfung'; +$this->phrasen['pruefung/sammelemailBodyTermin'] = 'Termin'; +$this->phrasen['pruefung/sammelemailBodyDauer'] = 'Dauer'; + $this->phrasen['pruefung/emailLektorSubjectAnmeldung'] = 'Anmeldung zur Prüfung'; $this->phrasen['pruefung/emailLektorStudentIn'] = 'StudentIn'; $this->phrasen['pruefung/emailLektorHatSichZurPruefung'] = 'hat sich zur Prüfung'; diff --git a/locale/en-US/pruefung.php b/locale/en-US/pruefung.php index 7b0cec97e..59d9953ca 100644 --- a/locale/en-US/pruefung.php +++ b/locale/en-US/pruefung.php @@ -78,6 +78,7 @@ $this->phrasen['pruefung/unbegrenzt'] = 'unlimited'; $this->phrasen['pruefung/bewertungenZu'] = 'Bewertungen zu'; $this->phrasen['pruefung/keineAuswahl'] = 'Keine Auswahl'; $this->phrasen['pruefung/alleBestaetigen'] = 'Confirm all'; +$this->phrasen['pruefung/bitteEmailAngeben'] = 'Please enter a valid E-Mail Address.'; // pruefungsbewertung $this->phrasen['pruefung/pruefungsbewertungTitle'] = 'Prüfungsbewertung'; @@ -140,6 +141,14 @@ $this->phrasen['pruefung/emailBodyOrt'] = 'Ort:'; $this->phrasen['pruefung/emailBodyLinkZurAnmeldung'] = 'Link zur Anmeldung'; $this->phrasen['pruefung/emailBodyBitteHtmlSicht'] = 'Bitte sehen Sie sich die Nachricht in HTML Sicht an, um den Link vollständig darzustellen.'; +// Sammelmail +$this->phrasen['pruefung/sammelemailBody'] = 'The exam registrations for %s on %s have been confirmed by %s.
    '; +$this->phrasen['pruefung/sammelemailBody2'] = 'Place: %s
    '; +$this->phrasen['pruefung/sammelemailBodyBestaetigt'] = 'Confirmed by'; +$this->phrasen['pruefung/sammelemailBodyPruefung'] = 'Exam'; +$this->phrasen['pruefung/sammelemailBodyTermin'] = 'Exam date'; +$this->phrasen['pruefung/sammelemailBodyDauer'] = 'Duration'; + $this->phrasen['pruefung/emailLektorSubjectAnmeldung'] = 'Anmeldung zur Prüfung'; $this->phrasen['pruefung/emailLektorStudentIn'] = 'StudentIn'; $this->phrasen['pruefung/emailLektorHatSichZurPruefung'] = 'hat sich zur Prüfung'; From 045b764d654d44229fc97112673f712013384659 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 26 Aug 2025 12:40:27 +0200 Subject: [PATCH 098/312] studentenverwaltung im infocenter verlinkt --- application/controllers/system/infocenter/InfoCenter.php | 2 ++ application/views/system/infocenter/infocenterDetails.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/application/controllers/system/infocenter/InfoCenter.php b/application/controllers/system/infocenter/InfoCenter.php index 1fc49c72f..1a4f566e6 100644 --- a/application/controllers/system/infocenter/InfoCenter.php +++ b/application/controllers/system/infocenter/InfoCenter.php @@ -362,6 +362,8 @@ class InfoCenter extends Auth_Controller $data[self::ORIGIN_PAGE] = $origin_page; $data[self::PREV_FILTER_ID] = $this->input->get(self::PREV_FILTER_ID); + $data['studiensemester'] = $this->variablelib->getVar('infocenter_studiensemester'); + $this->load->view('system/infocenter/infocenterDetails.php', $data); } diff --git a/application/views/system/infocenter/infocenterDetails.php b/application/views/system/infocenter/infocenterDetails.php index 51b913b6c..fb023c5fd 100644 --- a/application/views/system/infocenter/infocenterDetails.php +++ b/application/views/system/infocenter/infocenterDetails.php @@ -57,7 +57,7 @@
    From 1ef884de8c966613572f7523c4116db0fb57dd73 Mon Sep 17 00:00:00 2001 From: chfhtw Date: Tue, 26 Aug 2025 14:25:04 +0200 Subject: [PATCH 099/312] API: return results on multicall --- public/js/plugins/Api.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/js/plugins/Api.js b/public/js/plugins/Api.js index 091a81717..034c326e9 100644 --- a/public/js/plugins/Api.js +++ b/public/js/plugins/Api.js @@ -486,7 +486,7 @@ export default { if (Array.isArray(factory)) { const $api = app.config.globalProperties.$api; - Promise + return Promise .allSettled(factory.map((config, index) => { if (!Array.isArray(config)) config = ['#' + index, config]; @@ -526,6 +526,8 @@ export default { for (var errType in typedErrors) { errorConfig.handler[errType](typedErrors[errType]); } + + return result; }); } let { method, url, params, config } = factory; From f1c9e641bbe0b64d990cb026bc6ebaefc87494fa Mon Sep 17 00:00:00 2001 From: chfhtw Date: Tue, 26 Aug 2025 14:25:29 +0200 Subject: [PATCH 100/312] API: feedback for stv status dropdown --- .../Studentenverwaltung/Details/Status/Dropdown.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js b/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js index 8c2071c51..1f6f41900 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js @@ -151,7 +151,17 @@ export default { ApiStvStatus.addStudent(prestudent_id, data), { errorHeader: prestudent_id } ])) - .then(() => { + .then(result => { + const messagesSuccessful = result.filter(res => res.status == 'fulfilled'); + if (messagesSuccessful.length) { + this.$fhcAlert.alertDefault( + 'info', + 'Feedback', + messagesSuccessful.length + " erfolgreiche Statusänderung(en) durchgeführt", // TODO(chris): translate + false, + true + ); + } this.$emit('reloadTable'); this.$reloadList(); }); From e3665569929968383f1194d722bf20214e6a1d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96sterreicher?= Date: Tue, 26 Aug 2025 16:05:06 +0200 Subject: [PATCH 101/312] =?UTF-8?q?Dienstverhinderung=20f=C3=BCr=20Volkssc?= =?UTF-8?q?hultag=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cis/private/profile/zeitsperre_resturlaub.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cis/private/profile/zeitsperre_resturlaub.php b/cis/private/profile/zeitsperre_resturlaub.php index ffd179401..1b6546d41 100644 --- a/cis/private/profile/zeitsperre_resturlaub.php +++ b/cis/private/profile/zeitsperre_resturlaub.php @@ -285,15 +285,16 @@ function showHideBezeichnungDropDown() if (dd.options[dd.selectedIndex].value == 'DienstV') { var str = ''; sp.innerHTML = str; From e2147c8f1f4840cbd378dcdd3e030b2865dfc6f8 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 27 Aug 2025 08:40:57 +0200 Subject: [PATCH 102/312] regeln von kompatiblen lvs wird geprueft zusammen legen von pruefungsterminen ermoeglicht --- cis/private/lehre/pruefung/pruefung.js.php | 36 +++ .../pruefung/pruefungsanmeldung.json.php | 210 ++++++++++++++++++ .../pruefungsanmeldungen_verwalten.php | 30 +++ cis/private/profile/studienplan.php | 60 ++++- include/lehrveranstaltung.class.php | 30 +++ include/pruefungstermin.class.php | 16 ++ include/reservierung.class.php | 32 +++ 7 files changed, 406 insertions(+), 8 deletions(-) diff --git a/cis/private/lehre/pruefung/pruefung.js.php b/cis/private/lehre/pruefung/pruefung.js.php index 73b808d14..ea4c1eff0 100644 --- a/cis/private/lehre/pruefung/pruefung.js.php +++ b/cis/private/lehre/pruefung/pruefung.js.php @@ -1231,6 +1231,42 @@ function loadPruefungStudiengang(studiensemester) }); } +function terminezusammenlegen(termine, lv_id) +{ + if(termine.length <= 1) + return; + + $.ajax({ + dataType: 'json', + url: "./pruefungsanmeldung.json.php", + type: "POST", + data: { + method: "terminezusammenlegen", + 'termine[]': termine, + lv_id: lv_id + }, + error: loadError, + success: function(data){ + if(data.error === 'false') + { + loadPruefungStudiengang() + $("#anmeldung_hinzufuegen").empty(); + $("#lvdaten").empty(); + $("#anmeldeDaten").empty(); + $("#reihungSpeichernButton").empty(); + $("#kommentar").empty(); + $("#kommentarSpeichernButton").empty(); + $("#raumLink").empty(); + $("#listeDrucken").empty(); + } + else + { + messageBox("message", data.errormsg, "red", "highlight", 10000); + } + } + }); +} + /** * Zeigt das Formularfeld zur Eingabe eines Kommentars in der Anmeldungsverwaltung an. * @param {String} vorname Vorname des Studenten diff --git a/cis/private/lehre/pruefung/pruefungsanmeldung.json.php b/cis/private/lehre/pruefung/pruefungsanmeldung.json.php index c286f666e..f06898068 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldung.json.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldung.json.php @@ -110,6 +110,11 @@ switch($method) $studiensemester = filter_input(INPUT_POST,"studiensemester"); $data = getPruefungenStudiengangBySemester($studiensemester); break; + case 'terminezusammenlegen': + $termine = filter_input(INPUT_POST, 'termine', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY); + $lv_id = filter_input(INPUT_POST, 'lv_id'); + $data = terminezusammenlegen($termine, $lv_id); + break; case 'saveKommentar': $data = saveKommentar(); break; @@ -1285,6 +1290,211 @@ function getPruefungenStudiengangBySemester($aktStudiensemester) return $data; } +function terminezusammenlegen($termine, $lv_id) +{ + $result = array(); + $alle_termine = array(); + $error = false; + $terminkollision = defined('CIS_PRUEFUNGSANMELDUNG_ERLAUBE_TERMINKOLLISION') ? CIS_PRUEFUNGSANMELDUNG_ERLAUBE_TERMINKOLLISION : false; + foreach($termine as $termin) + { + $pruefungstermin = new pruefungstermin(); + $pruefungstermin->load($termin); + $pruefung = new pruefungCis(); + $pruefung->load($pruefungstermin->pruefung_id); + $pruefung->getLehrveranstaltungenByPruefung(); + + $lehrveranstaltungen = array_column($pruefung->lehrveranstaltungen, 'lehrveranstaltung_id'); + if (!in_array($lv_id, $lehrveranstaltungen)) + continue; + + $pruefung->lehrveranstaltung_id = $lv_id; + $pruefung->termin = $pruefungstermin; + $alle_termine[] = $pruefung; + } + + + if (count($alle_termine) >= 1) + { + usort($alle_termine, function($a, $b) { + return strcmp($a->termin->von, $b->termin->von); + }); + + $first_termin = $alle_termine[0]; + + $first_mitarbeiter = $first_termin->mitarbeiter_uid; + $first_date = date('Y-m-d', strtotime($first_termin->termin->von)); + $first_studiensemester = $first_termin->studiensemester_kurzbz; + $first_sammelklausur = $first_termin->termin->sammelklausur; + $first_ort = $first_termin->termin->ort_kurzbz; + $first_raum = $first_termin->termin->anderer_raum; + $first_lv = $first_termin->lehrveranstaltung_id; + $first_titel = $first_termin->titel; + + $max_von = strtotime($first_termin->termin->von); + $max_bis = strtotime($first_termin->termin->bis); + $teilnehmer_min = (int)$first_termin->termin->teilnehmer_min; + $teilnehmer_max = (int)$first_termin->termin->teilnehmer_max; + + + $prevEnd = $max_bis; + + foreach ($alle_termine as $termin) + { + if (date('Y-m-d', strtotime($termin->termin->von)) !== $first_date) + { + $data['errormsg'] = 'Nicht der gleiche Tag!'; + $error = true; + } + + if ($termin->mitarbeiter_uid !== $first_mitarbeiter) + { + $data['errormsg'] = 'Unterschiedliche Lektoren!'; + $error = true; + } + + if ($termin->studiensemester_kurzbz !== $first_studiensemester) + { + $data['errormsg'] = 'Unterschiedliche Studiensemester!'; + $error = true; + } + + if ($termin->termin->sammelklausur !== $first_sammelklausur) + { + $data['errormsg'] = 'Sammelklausur unterschiedlich!'; + $error = true; + } + + if (!($termin->termin->ort_kurzbz === $first_ort || $first_termin->termin->anderer_raum = $first_raum)) + { + $data['errormsg'] = 'Ort/Raum unterschiedlich!'; + $error = true; + } + + if ($termin->lehrveranstaltung_id !== $first_lv) + { + $data['errormsg'] = 'Lehrveranstaltungen unterscheiden sich!'; + $error = true; + } + + + $start = strtotime($termin->termin->von); + $max_von = min($max_von, $start); + $max_bis = max($max_bis, strtotime($termin->termin->bis)); + $teilnehmer_min = min($teilnehmer_min, (int)$termin->termin->teilnehmer_min); + $teilnehmer_max = max($teilnehmer_max, (int)$termin->termin->teilnehmer_max); + + if (($start - $prevEnd > 0) && $first_ort) + { + $stunde = new stunde(); + + $gapStartStr = date('Y-m-d H:i:s', $prevEnd); + $gapEndStr = date('Y-m-d H:i:s', $start); + + $gapStartArr = explode(' ', $gapStartStr); + $gapEndArr = explode(' ', $gapEndStr); + + $stunden = $stunde->getStunden($gapStartArr[1], $gapEndArr[1]); + + $reservierung = new reservierung(); + $reserviert = false; + + $reservierungs_stunden = $reservierung->getReservierungen($first_ort, $gapStartArr[0]); + + $need_stunden = array_diff($stunden, $reservierungs_stunden); + + foreach ($need_stunden as $h) + { + if ($reservierung->isReserviert($first_ort, $gapStartArr[0], $h)) + $reserviert = true; + } + + if (!$terminkollision && $reserviert && !$first_sammelklausur) + { + $error = true; + $data['errormsg'] = 'Kann nicht zusammengelegt werden, da der Raum reserviert ist'; + } + else + { + $reservierung->studiengang_kz = "0"; + $reservierung->ort_kurzbz = $first_ort; + $reservierung->uid = $first_mitarbeiter; + $reservierung->datum = $gapStartArr[0]; + $reservierung->titel = $first_titel; + if (strlen($first_titel) > 10) + { + $reservierung->titel = "Prüfung"; + } + $reservierung->beschreibung = "Prüfung"; + $reservierung->insertamum = date('Y-m-d G:i:s'); + $reservierung->insertvon = get_uid(); + $reservierungError = false; + + foreach ($need_stunden as $h) + { + $reservierung->stunde = $h; + if (!$reservierungError) + { + if (!$reservierung->save(true)) + { + $error = true; + $data['errormsg'] = $reservierung->errormsg; + $reservierungError = true; + } + } + } + } + } + $prevEnd = strtotime($termin->termin->bis); + } + + if (!$error) + { + $first_pruefungstermin = new pruefungstermin(); + $first_pruefungstermin->load($first_termin->termin->pruefungstermin_id); + + $first_pruefungstermin->von = date('Y-m-d H:i:s', $max_von); + $first_pruefungstermin->bis = date('Y-m-d H:i:s', $max_bis); + $first_pruefungstermin->teilnehmer_min = $teilnehmer_min; + $first_pruefungstermin->teilnehmer_max = $teilnehmer_max; + + $first_pruefungstermin->save(); + + $alle_termine = array_slice($alle_termine, 1); + + foreach ($alle_termine as $termin) + { + $anmeldung_termin = new pruefungsanmeldung(); + $anmeldungen_termine = $anmeldung_termin->getAnmeldungenByTermin($termin->termin->pruefungstermin_id); + + if (count($anmeldungen_termine) === 0) + { + $first_pruefungstermin->delete($termin->termin->pruefungstermin_id); + } + $i = 0; + $anmeldungen_termine_count = count($anmeldungen_termine); + foreach ($anmeldungen_termine as $anmeldungtermin) + { + $anmeldung = new pruefungsanmeldung(); + $anmeldung->load($anmeldungtermin->pruefungsanmeldung_id); + $old_pruefuengstermin_id = $anmeldung->pruefungstermin_id; + $anmeldung->pruefungstermin_id = $first_termin->termin->pruefungstermin_id; + if ($anmeldung->save(false) && ($i === $anmeldungen_termine_count - 1)) + { + $first_pruefungstermin->delete($old_pruefuengstermin_id); + } + $i ++; + } + } + } + } + + $data['result']= $result; + $data['error']= $error ? 'true' : 'false'; + $data['errormsg']=''; + return $data; +} + /** * * @return typespeichert ein Kommentar zu einer Prüfungsanmeldung diff --git a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php index 653d29c02..4928ed91a 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php @@ -258,6 +258,36 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung } }); + + $('#zusammenlegen').on('click', function() { + let ausgewaehlte_termine = $('.termin-checkbox:checked'); + + if (ausgewaehlte_termine.length === 0) + return; + + let erster_termin = ausgewaehlte_termine.first(); + let erstes_datum = erster_termin.data('datum'); + let erste_lvid = erster_termin.data('lv-id'); + + let termine = []; + ausgewaehlte_termine.each(function() {{ + let termin = $(this); + let datum = termin.data('datum'); + let lv_id = termin.data('lv-id'); + + if (erstes_datum !== datum) + return alert("Die ausgewählten Termine liegen nicht am selben Tag und können daher nicht zusammengelegt werden.") + if (erste_lvid !== lv_id) + return alert("Bei den ausgewählten Terminen handelt es sich um unterschiedliche Lehrveranstaltungen, die daher nicht zusammengelegt werden können.") + + termine.push(termin.data('termin-id')); + }}) + + if (termine.length > 0) + { + terminezusammenlegen(termine, erste_lvid); + } + }) });

    t('pruefung/anmeldungenVerwalten'); ?>

    diff --git a/cis/private/profile/studienplan.php b/cis/private/profile/studienplan.php index 92728df42..1bc8f3184 100644 --- a/cis/private/profile/studienplan.php +++ b/cis/private/profile/studienplan.php @@ -359,7 +359,7 @@ drawTree($tree,0); function drawTree($tree, $depth) { - global $uid, $stsem_arr, $noten_arr, $lvangebot_arr, $aktornext; + global $uid, $stsem_arr, $noten_arr, $lvangebot_arr, $aktornext, $studienplan_id; global $datum_obj, $db, $lv_arr, $p, $note_pruef_arr, $student; global $anrechnung; @@ -401,7 +401,10 @@ function drawTree($tree, $depth) echo '    '; $lvkompatibel = new lehrveranstaltung(); - $lvkompatibel_arr = $lvkompatibel->loadLVkompatibel($row_tree->lehrveranstaltung_id); + + $studienplaene_kompatibel = $lvkompatibel->loadLVkompatibelWithStudienplan($row_tree->lehrveranstaltung_id, $studienplan_id); + + $lvkompatibel_arr = array_unique(array_column($studienplaene_kompatibel, 'lehrveranstaltung_id_kompatibel')); $lvkompatibel_arr[]=$row_tree->lehrveranstaltung_id; $abgeschlossen=false; @@ -414,6 +417,24 @@ function drawTree($tree, $depth) else $abgeschlossen=false; } + else if (!empty($studienplaene_kompatibel)) + { + foreach ($studienplaene_kompatibel as $kompatibel) + { + $lvregelExists = $lvregel->exists($kompatibel['studienplan_lehrveranstaltung_id']); + + if ($lvregelExists) + { + if ($lvregel->isAbgeschlossen($uid, $kompatibel['studienplan_lehrveranstaltung_id'])) + $abgeschlossen=true; + else + $abgeschlossen=false; + + break; + } + } + } + $lvinfo = new lvinfo(); switch(getSprache()) { @@ -583,15 +604,38 @@ function drawTree($tree, $depth) { $semesterlock=true; } - else + + if (!$semesterlock && !empty($studienplaene_kompatibel)) { - //check if rules are fulfilled just for actual or next studiensemester - if($stsem === $aktornext) + foreach ($studienplaene_kompatibel as $kompatibel) { - $result = $lvregel->isZugangsberechtigt($uid, $row_tree->studienplan_lehrveranstaltung_id, $stsem); - if((is_array($result)) && ($result[0] !== true)) + if (!$lvregel->checkSemester($kompatibel['studienplan_lehrveranstaltung_id'], $semester)) { - $regelerfuellt=false; + $semesterlock = true; + break; + } + } + } + + //check if rules are fulfilled just for actual or next studiensemester + if($stsem === $aktornext && !$semesterlock) + { + $result = $lvregel->isZugangsberechtigt($uid, $row_tree->studienplan_lehrveranstaltung_id, $stsem); + if((is_array($result)) && ($result[0] !== true)) + { + $regelerfuellt = false; + } + + if ($regelerfuellt && !empty($studienplaene_kompatibel)) + { + foreach ($studienplaene_kompatibel as $kompatibel) + { + $result = $lvregel->isZugangsberechtigt($uid, $kompatibel['studienplan_lehrveranstaltung_id'], $stsem); + if (is_array($result) && $result[0] !== true) + { + $regelerfuellt = false; + break; + } } } } diff --git a/include/lehrveranstaltung.class.php b/include/lehrveranstaltung.class.php index 48edd9d0a..e288f88e6 100644 --- a/include/lehrveranstaltung.class.php +++ b/include/lehrveranstaltung.class.php @@ -1554,6 +1554,36 @@ class lehrveranstaltung extends basis_db } } + public function loadLVkompatibelWithStudienplan($lehrveranstaltung_id, $studienplan_id) + { + if (!is_numeric($lehrveranstaltung_id)) + { + $this->errormsg = 'Lehrveranstaltung_id muss eine gueltige Zahl sein'; + return false; + } + if (!is_numeric($studienplan_id)) + { + $this->errormsg = 'Studienplan_id muss eine gueltige Zahl sein'; + return false; + } + + $qry = "SELECT studienplan_lehrveranstaltung_id, lehrveranstaltung_id_kompatibel + FROM lehre.tbl_lehrveranstaltung_kompatibel + JOIN lehre.tbl_studienplan_lehrveranstaltung ON lehrveranstaltung_id_kompatibel = tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id + WHERE tbl_lehrveranstaltung_kompatibel.lehrveranstaltung_id=".$this->db_add_param($lehrveranstaltung_id, FHC_INTEGER)." + ;"; + + if($this->db_query($qry)) + { + $data = array(); + while($row = $this->db_fetch_object()) + { + $data[] = ['lehrveranstaltung_id_kompatibel' => $row->lehrveranstaltung_id_kompatibel, 'studienplan_lehrveranstaltung_id' => $row->studienplan_lehrveranstaltung_id]; + } + return $data; + } + } + /** * Lädt alle kompatiblen LVs zu einer Lehrveranstaltung * @param $lehrveranstaltung_id ID der Lehrveranstaltung diff --git a/include/pruefungstermin.class.php b/include/pruefungstermin.class.php index c7eb0b085..80db93338 100644 --- a/include/pruefungstermin.class.php +++ b/include/pruefungstermin.class.php @@ -244,4 +244,20 @@ class pruefungstermin extends basis_db{ } } + + public function delete($pruefungstermin_id) + { + $qry = "DELETE FROM campus.tbl_pruefungstermin WHERE pruefungstermin_id=".$this->db_add_param($pruefungstermin_id).";"; + + if($this->db_query($qry)) + { + return true; + } + else + { + $this->errormsg='Fehler beim Löschen des Eintrages'; + return false; + } + } + } diff --git a/include/reservierung.class.php b/include/reservierung.class.php index 44d989600..a29b314dc 100644 --- a/include/reservierung.class.php +++ b/include/reservierung.class.php @@ -322,5 +322,37 @@ class reservierung extends basis_db } } + + public function getReservierungen($ort_kurzbz, $datum) + { + $datum_obj = new Datum(); + if(!$datum_obj->checkDatum($datum)) + { + $this->errormsg='Datum hat ein ungueltiges Format'; + return false; + } + + $qry = "SELECT stunde FROM campus.tbl_reservierung + WHERE + ort_kurzbz=".$this->db_add_param($ort_kurzbz)." AND + datum=".$this->db_add_param($datum); + + + if($this->db_query($qry)) + { + $result = array(); + while($row = $this->db_fetch_object()) + { + $result[] = $row->stunde; + } + } + else + { + $this->errormsg = "Prüfungstypen konnten nicht geladen werden."; + return false; + } + + return $result; + } } ?> From 7e6643c0ff6e6bc7fbdaa4864b91afb56a717e4b Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 27 Aug 2025 09:51:34 +0200 Subject: [PATCH 103/312] phrase typo --- public/js/components/LVVerwaltung/Lektor/Table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/LVVerwaltung/Lektor/Table.js b/public/js/components/LVVerwaltung/Lektor/Table.js index 373a8955c..237554be9 100644 --- a/public/js/components/LVVerwaltung/Lektor/Table.js +++ b/public/js/components/LVVerwaltung/Lektor/Table.js @@ -199,7 +199,7 @@ export default{ table-only :side-menu="false" reload - :new-btn-label="$p.t('lehre', 'addlektor')" + :new-btn-label="$p.t('lehre', 'addLektor')" new-btn-show @click:new="showAutocomplete = !showAutocomplete" > From 41d26246aafafc48dd1f74f72a70f24c2da0390f Mon Sep 17 00:00:00 2001 From: chfhtw Date: Wed, 27 Aug 2025 10:19:39 +0200 Subject: [PATCH 104/312] Form Input Component: add name & use autocomplete that was loaded in the CI View --- public/js/components/Form/Input.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/js/components/Form/Input.js b/public/js/components/Form/Input.js index c85272aab..81b47d504 100644 --- a/public/js/components/Form/Input.js +++ b/public/js/components/Form/Input.js @@ -3,6 +3,7 @@ import FhcFragment from "../Fragment.js"; let _uuid = {}; export default { + name: "FormInput", inheritAttrs: false, components: { FhcFragment @@ -220,7 +221,7 @@ export default { if (this.tag == 'VueDatePicker' && !this._.components.VueDatePicker) { this._.components.VueDatePicker = Vue.defineAsyncComponent(() => import("../vueDatepicker.js.php")); } else if (this.tag == 'PvAutocomplete' && !this._.components.PvAutocomplete) { - this._.components.PvAutocomplete = Vue.defineAsyncComponent(() => import(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/public/js/components/primevue/autocomplete/autocomplete.esm.min.js")); + this._.components.PvAutocomplete = primevue.autocomplete; } else if (this.tag == 'UploadImage' && !this._.components.UploadImage) { this._.components.UploadImage = Vue.defineAsyncComponent(() => import("./Upload/Image.js")); } else if (this.tag == 'UploadDms' && !this._.components.UploadDms) { From 2d2bbd1bb373bc99a7d7fa647162b6fb663f17d3 Mon Sep 17 00:00:00 2001 From: chfhtw Date: Wed, 27 Aug 2025 10:20:41 +0200 Subject: [PATCH 105/312] Bootstrap Modal Component: forward shown-bs-modal event --- public/js/components/Bootstrap/Modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/components/Bootstrap/Modal.js b/public/js/components/Bootstrap/Modal.js index 61c5c6857..a84d9d8d7 100644 --- a/public/js/components/Bootstrap/Modal.js +++ b/public/js/components/Bootstrap/Modal.js @@ -130,7 +130,7 @@ export default { }); }); }, - template: `
    - +
    From 7df383f45ee01a4237c590ad4d869a96b6b0c042 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 28 Aug 2025 08:36:20 +0200 Subject: [PATCH 110/312] hinzugefuegte spann class caps durch den parser entfernt --- application/libraries/PhrasesLib.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/libraries/PhrasesLib.php b/application/libraries/PhrasesLib.php index ecd8094d6..647bf7074 100644 --- a/application/libraries/PhrasesLib.php +++ b/application/libraries/PhrasesLib.php @@ -122,6 +122,7 @@ class PhrasesLib $tmpText = substr($tmpText, 0, strlen($tmpText) - 4); } } + $tmpText = str_replace(['', ''], '', $tmpText); $result->retval[$i]->text = $tmpText; } From 7b187ebadd42c3ad3f39821031a15df93deb046f Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 28 Aug 2025 11:05:04 +0200 Subject: [PATCH 111/312] add Dropdown for Multiaction Print add Event for adding Documents to PrintArray add Berechtigung hasPermissionOutputformat to enable Print for odt and doc Formats (documents abschlusspruefung) add Validations --- .../api/frontend/v1/stv/Dokumente.php | 184 ++++++------------ public/js/api/factory/stv/documents.js | 5 +- .../Stv/Studentenverwaltung/Details/Archiv.js | 12 +- .../Details/Archiv/DocumentDropdown.js | 95 ++++----- 4 files changed, 103 insertions(+), 193 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Dokumente.php b/application/controllers/api/frontend/v1/stv/Dokumente.php index 520f5d824..17be3b9df 100644 --- a/application/controllers/api/frontend/v1/stv/Dokumente.php +++ b/application/controllers/api/frontend/v1/stv/Dokumente.php @@ -572,29 +572,21 @@ class Dokumente extends FHCAPI_Controller public function getDocumentDropDown($prestudent_id, $studiensemester_kurzbz, $studiengang_kz) { - //TODO(Manu) Berechtigungen hasPermissionOutputformat - //TODO(Manu) remove: just for test ouput - $hasPermissionOutputformat = false; + //permission to create also odt, and doc outputs of certain documents(menu abschlusspruefung) + $hasPermissionOutputformat = $this->permissionlib->isBerechtigt('system/change_outputformat', 's'); - //TODO(Manu) Validierungen - if (!$prestudent_id) { - $this->terminateWithError('Prestudent id is required.'); - } + if (!$prestudent_id) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Prestudent_id']), self::ERROR_TYPE_GENERAL); if (!$studiensemester_kurzbz) - $this->terminateWithError("kein Studiensemester"); - if (!$studiengang_kz) - $this->terminateWithError("kein Studiengang_kz"); + $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); - //TODO(Manu) check if if Array[0] bis Array[4] befüllt - //TODO(Manu) handling stgTyp ungleich b,m,d - - // $semString = implode(";", $semArray); - // $this->terminateWithError("Semester " . $semString . " " . $semArray[0] . " " . $semArray[1]); - $documents = [ $this->buildDropdownEntry("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uid, 10, null), $this->buildDropdownEntry("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uid, 20, null), @@ -605,7 +597,6 @@ class Dokumente extends FHCAPI_Controller $this->buildDropdownEntry("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uid, 60, null), $this->buildDropdownEntry("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uid, 61, null), - // Studienerfolg Menüs automatisch $this->buildStudienerfolgSubmenu("de", $uid, $semArray, $studiensemester_kurzbz), $this->buildStudienerfolgSubmenu("en", $uid, $semArray, $studiensemester_kurzbz), $this->buildStudienerfolgSubmenu("de", $uid, $semArray, $studiensemester_kurzbz, true), @@ -617,7 +608,7 @@ class Dokumente extends FHCAPI_Controller "name" => "Verwaltung des StudierendenStatus", "order" => 110, "data" => [ - $this->buildDropdownEntry("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldungl&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), + $this->buildDropdownEntry("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), $this->buildDropdownEntry("Abmeldung durch Stgl", "AntragAbmeldungStgl", "xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), $this->buildDropdownEntry("Unterbrechung", "Unterbrechung", "xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), $this->buildDropdownEntry("Wiederholung", "Abmeldung durch Ablauf der Wiederholungsfrist", "xml=AntragWiederholung.xml.php&xsl=AntragWiederholung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), @@ -651,8 +642,11 @@ class Dokumente extends FHCAPI_Controller return $documents || null; } - public function getDocumentDropDownMulti() + 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 = []; @@ -661,134 +655,71 @@ class Dokumente extends FHCAPI_Controller $prestudent_id = $this-> _loadPrestudentFromUid($uid); $prestudentIds[] = $prestudent_id; } - } else { - echo "No prestudent IDs received."; } + 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 = [ - [ - "id" => "accountinfo1", - "type" => "documenturl", - "name" => "Accountinfoblatt", - "url" => "pdfExport.php?xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "ausbildungsvertrag1_de", - "type" => "documenturl", - "name" => "Ausbildungsvertrag Deutsch", - "url" => "pdfExport.php?xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "ausbildungsvertrag1_en", - "type" => "documenturl", - "name" => "Ausbildungsvertrag Englisch", - "url" => "pdfExport.php?xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "submenu_studienerfolg_1", - "type" => "submenu", - "name" => "Studienerfolg", - "data" => [ - [ - "id" => "submenu_studienerfolg_sem1", - "type" => "submenu", - "name" => "Studienerfolg WS2025", - "data" => [ - [ - "id" => "studienerfolg_sem_alle_1", - "type" => "submenu", - "name" => "Studienerfolg WS2025 Alle", - "data" => [ - [ - "id" => "studienerfolg_sem_alle_1_FA", - "type" => "documenturl", - "name" => "Studienerfolg Alle FINANZAMT", - "url" => "pdfExport.php?xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "studienerfolg_sem_alle_1_nichtFA", - "type" => "documenturl", - "name" => "Studienerfolg Alle NICHT FINANZAMT", - "url" => "pdfExport.php?xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ] - ] + $this->buildDropdownEntry("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uidString, 10, null), + $this->buildDropdownEntry("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uidString, 20, null), + $this->buildDropdownEntry("ausbildungsvertrag_en", "Ausbildungsvertrag Englisch", "xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf", $uidString, 21, null), + $this->buildDropdownEntry("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uidString, 40, null), + $this->buildDropdownEntry("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uidString, 41, null), + $this->buildDropdownEntry("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uidString", $uidString,100, "zutrittskarte.php"), + $this->buildDropdownEntry("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uidString, 60, null), + $this->buildDropdownEntry("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", + "id" => "submenu_studstatus", "type" => "submenu", "name" => "Verwaltung des StudierendenStatus", + "order" => 110, "data" => [ - [ - "id" => "Abmeldung", - "type" => "documenturl", - "name" => "Abmeldung", - "url" => "pdfExport.php?xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "Abmeldung durch Stg", - "type" => "documenturl", - "name" => "AntragAbmeldungStgl", - "url" => "pdfExport.php?xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ] + $this->buildDropdownEntry("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), + $this->buildDropdownEntry("Abmeldung durch Stgl", "AntragAbmeldungStgl", "xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), + $this->buildDropdownEntry("Unterbrechung", "Unterbrechung", "xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), + $this->buildDropdownEntry("Wiederholung", "Abmeldung durch Ablauf der Wiederholungsfrist", "xml=AntragWiederholung.xml.php&xsl=AntragWiederholung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), ] ], - [ - "id" => "zutrittskarte", - "type" => "documenturl", - "name" => "Zutrittskarte", - "url" => "zutrittskarte.php?xsl=ZutrittskarteStud&output=pdf&data=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "zutrittskarte", - "type" => "parameterurl", - "name" => "Zutrittskarte", - "baseurl" => "zutrittskarte.php", - "parameterurl" =>"xsl=ZutrittskarteStud&output=pdf&data=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "studienbestaetigung", - "type" => "documenturl", - "name" => "Studienbestätigung", - "url" => "pdfExport.php?xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], - [ - "id" => "studienerfolg", - "type" => "documenturl", - "name" => "Studienbestätigung", - "url" => "pdfExport.php?xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf&uid=" . $uidString, - "scope" => "prestudent" - ], + $this->loadDropDownEntriesFinalExam($hasPermissionOutputformat, $stgTyp, $uidString), + + //TODO(Manu) also in Fas multi not working +/* $this->buildDropdownEntry("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uidString, 80, null), + */ + + $this->buildDropdownEntry("diplomasupp", "Diploma Supplement (nur Voransicht)", "xml=diplomasupplement.xml.php&xsl_stg_kz=$studiengang_kz&xsl=DiplSupplement&output=pdf", $uidString, 81, null) ]; -/* Events::trigger('DocumentGenerationDropDownMulti', + Events::trigger('DocumentGenerationDropDownMulti', // passing $menu per reference function & () use (&$documents) { return $documents; }, - $prestudent_id, + $studentUids, $studiensemester_kurzbz, $studiengang_kz - );*/ + ); usort($documents, function ($a, $b) { $orderA = isset($a['order']) ? (int)$a['order'] : PHP_INT_MAX; @@ -796,7 +727,6 @@ class Dokumente extends FHCAPI_Controller return $orderA <=> $orderB; }); - // FireEvent(DocumentGenerationDropDownMulti(&$documents); $this->terminateWithSuccess($documents); @@ -977,9 +907,7 @@ class Dokumente extends FHCAPI_Controller else if ($stgTyp == 'm' || $stgTyp == 'd') $postfix = 'Master'; else - //TODO(Manu) sollte nicht null sein!! -> dropdown wird im Falle von Lehrgängen nicht erstellt - return null; - + return []; $arrayFinalExam = [ 'pruefungsprotokoll' => [ diff --git a/public/js/api/factory/stv/documents.js b/public/js/api/factory/stv/documents.js index 37ec8298b..633afcba1 100644 --- a/public/js/api/factory/stv/documents.js +++ b/public/js/api/factory/stv/documents.js @@ -74,16 +74,15 @@ export default { }; }, getDocumentDropdown(params){ - console.log(params); return { method: 'get', url: 'api/frontend/v1/stv/dokumente/getDocumentDropDown/' + params.prestudent_id + '/' + params.studiensemester_kurzbz + '/' + params.studiengang_kz, }; }, - getDocumentDropdownMulti(studentUids){ + getDocumentDropdownMulti(studentUids, params){ return { method: 'get', - url: 'api/frontend/v1/stv/dokumente/getDocumentDropDownMulti/', + url: 'api/frontend/v1/stv/dokumente/getDocumentDropDownMulti/' + params.studiensemester_kurzbz + '/' + params.studiengang_kz, params: {studentUids} }; } diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js index f439d0135..dabb000d1 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -224,14 +224,11 @@ export default { return retval; }, showDropDownMulti(){ - if (this.modelValue.length) { - console.log("started comp property showDropdown Multi " + this.modelValue.length); return true; } return false; } - }, watch: { modelValue() { @@ -308,17 +305,18 @@ export default { .catch(this.$fhcAlert.handleSystemError); if (this.modelValue.length) { - console.log("multi" + this.modelValue.length); - console.log(this.studentUids); + const params = { + studiensemester_kurzbz: this.currentSemester, + studiengang_kz: this.stg_kz + }; this.$api - .call(ApiStvDocuments.getDocumentDropdownMulti(this.studentUids)) + .call(ApiStvDocuments.getDocumentDropdownMulti(this.studentUids, params)) .then(result => { console.log(result); this.documentDropdownObject = result; }) .catch(this.$fhcAlert.handleSystemError); } else { - console.log("single"); const params = { prestudent_id: this.modelValue.prestudent_id, studiensemester_kurzbz: this.currentSemester, diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv/DocumentDropdown.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv/DocumentDropdown.js index 215a1f1ca..001a45ed8 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv/DocumentDropdown.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv/DocumentDropdown.js @@ -34,7 +34,6 @@ export default { printDokument(url, scope){ //TODO Manu(check if logic not in content (Zutrittkarte also in content folder)) let linkToPdf = this.cisRoot + 'content/' + url; - console.log("in print " + linkToPdf + " scope" + scope); window.open(linkToPdf, '_blank'); } }, @@ -42,71 +41,57 @@ export default {
    - -
    From 3cdb391a6da2336722d391ea09fa07e441af20e7 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Thu, 28 Aug 2025 14:29:26 +0200 Subject: [PATCH 112/312] use helper file instead of private function for building dropdown entry --- .../api/frontend/v1/stv/Dokumente.php | 108 ++++++------------ application/helpers/hlp_common_helper.php | 42 +++++++ .../Stv/Studentenverwaltung/Details/Archiv.js | 2 - 3 files changed, 76 insertions(+), 76 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Dokumente.php b/application/controllers/api/frontend/v1/stv/Dokumente.php index 17be3b9df..e6f6f4351 100644 --- a/application/controllers/api/frontend/v1/stv/Dokumente.php +++ b/application/controllers/api/frontend/v1/stv/Dokumente.php @@ -572,6 +572,7 @@ class Dokumente extends FHCAPI_Controller 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'); @@ -588,14 +589,14 @@ class Dokumente extends FHCAPI_Controller $stgTyp = $this->_getStudiengangstyp($studiengang_kz); $documents = [ - $this->buildDropdownEntry("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uid, 10, null), - $this->buildDropdownEntry("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uid, 20, null), - $this->buildDropdownEntry("ausbildungsvertrag_en", "Ausbildungsvertrag Englisch", "xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf", $uid, 21, null), - $this->buildDropdownEntry("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uid, 40, null), - $this->buildDropdownEntry("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uid, 41, null), - $this->buildDropdownEntry("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uid", $uid,100, "zutrittskarte.php"), - $this->buildDropdownEntry("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uid, 60, null), - $this->buildDropdownEntry("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uid, 61, null), + 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), @@ -608,17 +609,17 @@ class Dokumente extends FHCAPI_Controller "name" => "Verwaltung des StudierendenStatus", "order" => 110, "data" => [ - $this->buildDropdownEntry("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), - $this->buildDropdownEntry("Abmeldung durch Stgl", "AntragAbmeldungStgl", "xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), - $this->buildDropdownEntry("Unterbrechung", "Unterbrechung", "xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), - $this->buildDropdownEntry("Wiederholung", "Abmeldung durch Ablauf der Wiederholungsfrist", "xml=AntragWiederholung.xml.php&xsl=AntragWiederholung&prestudent_id=$prestudent_id&output=pdf", $uid, null, null), + 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), - $this->buildDropdownEntry("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uid, 80, null), - $this->buildDropdownEntry("diplomasupp", "Diploma Supplement (nur Voransicht)", "xml=diplomasupplement.xml.php&xsl_stg_kz=$studiengang_kz&xsl=DiplSupplement&output=pdf", $uid, 81, null) + 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', @@ -674,14 +675,14 @@ class Dokumente extends FHCAPI_Controller $stgTyp = $this->_getStudiengangstyp($studiengang_kz); $documents = [ - $this->buildDropdownEntry("accountinfo", "Accountinfoblatt", "xml=accountinfoblatt.xml.php&xsl=AccountInfo&output=pdf", $uidString, 10, null), - $this->buildDropdownEntry("ausbildungsvertrag", "Ausbildungsvertrag", "xml=ausbildungsvertrag.xml.php&xsl=Ausbildungsver&output=pdf", $uidString, 20, null), - $this->buildDropdownEntry("ausbildungsvertrag_en", "Ausbildungsvertrag Englisch", "xml=ausbildungsvertrag.xml.php&xsl=AusbVerEng&output=pdf", $uidString, 21, null), - $this->buildDropdownEntry("studienbestaetigung", "Studienbestätigung", "xml=student.rdf.php&xsl=Inskription&output=pdf", $uidString, 40, null), - $this->buildDropdownEntry("studienbestaetigung_en", "Studienbestätigung Englisch", "xml=student.rdf.php&xsl=InskriptionEng&output=pdf", $uidString, 41, null), - $this->buildDropdownEntry("zutrittskarte", "Zutrittskarte", "xsl=ZutrittskarteStud&output=pdf&data=$uidString", $uidString,100, "zutrittskarte.php"), - $this->buildDropdownEntry("studienblatt", "Studienblatt", "xml=studienblatt.xml.php&xsl=Studienblatt&output=pdf&ss=$studiensemester_kurzbz", $uidString, 60, null), - $this->buildDropdownEntry("studienblatt_eng", "Studienblatt Englisch", "xml=studienblatt.xml.php&xsl=StudienblattEng&output=pdf&ss=$studiensemester_kurzbz", $uidString, 61, null), + 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), @@ -695,20 +696,20 @@ class Dokumente extends FHCAPI_Controller "name" => "Verwaltung des StudierendenStatus", "order" => 110, "data" => [ - $this->buildDropdownEntry("Abmeldung", "Abmeldung", "xml=AntragAbmeldung.xml.php&xsl=AntragAbmeldung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), - $this->buildDropdownEntry("Abmeldung durch Stgl", "AntragAbmeldungStgl", "xml=AntragAbmeldungStgl.xml.php&xsl=AntragAbmeldungStgl&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), - $this->buildDropdownEntry("Unterbrechung", "Unterbrechung", "xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), - $this->buildDropdownEntry("Wiederholung", "Abmeldung durch Ablauf der Wiederholungsfrist", "xml=AntragWiederholung.xml.php&xsl=AntragWiederholung&prestudent_id=$prestudentIdsString&output=pdf", $uidString, null, null), + 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 -/* $this->buildDropdownEntry("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uidString, 80, null), +/* buildDropdownEntryPrintArray("bescheid", "Bescheid (nur Voransicht)", "xml=abschlusspruefung.rdf.php&xsl_stg_kz=$studiengang_kz&xsl=Bescheid&output=pdf", $uidString, 80, null), */ - $this->buildDropdownEntry("diplomasupp", "Diploma Supplement (nur Voransicht)", "xml=diplomasupplement.xml.php&xsl_stg_kz=$studiengang_kz&xsl=DiplSupplement&output=pdf", $uidString, 81, 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', @@ -806,47 +807,6 @@ class Dokumente extends FHCAPI_Controller return $typStudiengang; } - //TODO(Manu) make helperfunction - /** - * is building an array for Dropdown Entry in Print Dropdown - * @param $id id for the Document to add to the Document Array - * @param $name name of the dropdownEntry - * @param $parameterUrl url of parameters xml, xsl,format 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 - */ - private function buildDropdownEntry($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 - ]; - - } - /** * helper function to create ArrayStructure * actual studiensemester plus the 5 studiensemester in the past @@ -860,7 +820,7 @@ class Dokumente extends FHCAPI_Controller $xsl = $lang === "de" ? "Studienerfolg" : "StudienerfolgEng"; $idPrefix = "submenu_studienerfolg_" . $lang . ($fa ? "_fa" : ""); - $entries[] = $this->buildDropdownEntry( + $entries[] = buildDropdownEntryPrintArray( $idPrefix . "_aktuell", "ausgewähltes Semester", "xml=studienerfolg.rdf.php&xsl=$xsl&ss=$studiensemester_kurzbz" . ($fa ? "&typ=finanzamt" : ""), @@ -868,7 +828,7 @@ class Dokumente extends FHCAPI_Controller ); //all semester - $entries[] = $this->buildDropdownEntry( + $entries[] = buildDropdownEntryPrintArray( $idPrefix . "_all", "alle Semester", "xml=studienerfolg.rdf.php&xsl=$xsl&ss=$studiensemester_kurzbz&all=true" . ($fa ? "&typ=finanzamt" : ""), @@ -877,7 +837,7 @@ class Dokumente extends FHCAPI_Controller //sem from array foreach ($semArray as $i => $sem) { - $entries[] = $this->buildDropdownEntry( + $entries[] = buildDropdownEntryPrintArray( $idPrefix . ($i === 0 ? "_akt" : "_minus" . $i), $sem, "xml=studienerfolg.rdf.php&xsl=$xsl&ss=$sem" . ($fa ? "&typ=finanzamt" : ""), @@ -965,7 +925,7 @@ class Dokumente extends FHCAPI_Controller //3 outputformates foreach (["pdf", "odt", "docx"] as $format) { - $submenuData[] = $this->buildDropdownEntry( + $submenuData[] = buildDropdownEntryPrintArray( $idPrefix . "_" . $format, $baseName . " (" . strtoupper($format) . ")", $baseUrl . "&output=" . $format, @@ -986,7 +946,7 @@ class Dokumente extends FHCAPI_Controller $url = "xml=abschlusspruefung.rdf.php&xsl=" . $xsl . "&output=pdf"; - $submenuData[] = $this->buildDropdownEntry($id, $name, $url, $uid); + $submenuData[] = buildDropdownEntryPrintArray($id, $name, $url, $uid); } } } diff --git a/application/helpers/hlp_common_helper.php b/application/helpers/hlp_common_helper.php index 00c0a1b93..ad030c075 100644 --- a/application/helpers/hlp_common_helper.php +++ b/application/helpers/hlp_common_helper.php @@ -515,3 +515,45 @@ function has_permissions_for_stg($studiengang_kz, $permissions = '') return false; } + +/** + * 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 + ]; + +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js index dabb000d1..a3ac6593b 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -312,7 +312,6 @@ export default { this.$api .call(ApiStvDocuments.getDocumentDropdownMulti(this.studentUids, params)) .then(result => { - console.log(result); this.documentDropdownObject = result; }) .catch(this.$fhcAlert.handleSystemError); @@ -325,7 +324,6 @@ export default { this.$api .call(ApiStvDocuments.getDocumentDropdown(params)) .then(result => { - console.log(result); this.documentDropdownObject = result; }) .catch(this.$fhcAlert.handleSystemError); From d17f87be09fb2f43441f43a639934ec256642652 Mon Sep 17 00:00:00 2001 From: nscbrandb Date: Fri, 29 Aug 2025 10:35:27 +0200 Subject: [PATCH 113/312] also take environment config into account --- application/config/routes.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/application/config/routes.php b/application/config/routes.php index f6eafbcd2..b4ce2ed4a 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -111,23 +111,26 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/prestudent/(:num)'] = 'a // // (studiensemester_kurzbz)/person/(person_id) $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/person/(:num)'] = 'api/frontend/v1/stv/Students/getPerson/$1/$2'; -// load routes from extensions -$subdir = 'application/config/extensions'; -$dirlist = scandir($subdir); +// load routes from extensions, also look for environment-specific configs +$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions']; -if ($dirlist) +foreach($subdirs as $subdir) { - $files = array_diff($dirlist, array('.','..')); - - foreach ($files as &$item) + $dirlist = scandir($subdir); + if ($dirlist) { - if (is_dir($subdir . DIRECTORY_SEPARATOR . $item)) - { - $routes_file = $subdir . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'routes.php'; + $files = array_diff($dirlist, array('.','..')); - if (file_exists($routes_file)) + foreach ($files as &$item) + { + if (is_dir($subdir . DIRECTORY_SEPARATOR . $item)) { - require($routes_file); + $routes_file = $subdir . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'routes.php'; + + if (file_exists($routes_file)) + { + require($routes_file); + } } } } From 34528bb01446fb9f522589a63b2e310d4037f3c5 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Fri, 29 Aug 2025 13:19:00 +0200 Subject: [PATCH 114/312] iframe content hinzugefuegt --- public/js/components/Cis/Cms/Content.js | 5 ++ system/dbupdate_3.4.php | 1 + .../63436_cis4_iframe_component.php | 81 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 system/dbupdate_3.4/63436_cis4_iframe_component.php diff --git a/public/js/components/Cis/Cms/Content.js b/public/js/components/Cis/Cms/Content.js index 470a0fa62..ec1bc8a76 100644 --- a/public/js/components/Cis/Cms/Content.js +++ b/public/js/components/Cis/Cms/Content.js @@ -2,6 +2,8 @@ import raum_contentmittitel from './Content_types/Raum_contentmittitel.js' import general from './Content_types/General.js' import BsConfirm from "../../Bootstrap/Confirm.js"; import news_content from './Content_types/News_content.js'; +import iframe_content from './Content_types/Iframe_content.js'; + import ApiCms from '../../../api/factory/cms.js'; export default { @@ -24,6 +26,7 @@ export default { raum_contentmittitel, news_content, general, + iframe_content }, data() { return { @@ -83,6 +86,8 @@ export default { return "raum_contentmittitel"; case "news": return "news_content"; + case "iframe": + return "iframe_content"; default: return "general"; }; diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index cf2f40ca8..23ad5ffcd 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -77,6 +77,7 @@ require_once('dbupdate_3.4/55614_perm_verwaltetoe.php'); require_once('dbupdate_3.4/25999_C4_dashboard.php'); require_once('dbupdate_3.4/61730_Dashboard_Anpassungen.php'); require_once('dbupdate_3.4/40128_search.php'); +require_once('dbupdate_3.4/63436_cis4_iframe_component.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

    Pruefe Tabellen und Attribute!

    '; diff --git a/system/dbupdate_3.4/63436_cis4_iframe_component.php b/system/dbupdate_3.4/63436_cis4_iframe_component.php new file mode 100644 index 000000000..abff4b7e1 --- /dev/null +++ b/system/dbupdate_3.4/63436_cis4_iframe_component.php @@ -0,0 +1,81 @@ + + + + + + + + + +EOD; + +$xslt_xhtml= << + + + + + + + +
    Keine URL im Inhalt gefunden.
    +
    +
    +
    + +EOD; + +$xslt_xhtml_c4= << + + + + + + + +
    Keine URL im Inhalt gefunden.
    +
    +
    +
    + +EOD; + + +if ($result = @$db->db_query("SELECT * FROM campus.tbl_template WHERE template_kurzbz='iframe2'")) +{ + if ($db->db_num_rows($result) == 0) + { + $sql= <<db_query($sql)) + { + echo 'campus.tbl_template: ' . $db->db_last_error() . '
    '; + } + else + { + echo ' campus.tbl_template: Template "iframe" hinzugefügt.
    '; + } + + } +} \ No newline at end of file From 0d1cf2b04310cd6995542f942f674782a6cb1b6f Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 1 Sep 2025 09:20:33 +0200 Subject: [PATCH 115/312] autocomplete bei den kompatiblen lvs beruecksichtigen --- .../pruefungsanmeldungen_verwalten.php | 3 ++ system/dbupdate_3.4.php | 1 - ...hrveranstaltung_kompatibel_vergleichen.php | 46 ++++++++++++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php index 4928ed91a..6a1dc9a92 100644 --- a/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php +++ b/cis/private/lehre/pruefung/pruefungsanmeldungen_verwalten.php @@ -288,6 +288,8 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung terminezusammenlegen(termine, erste_lvid); } }) + + loadPruefungStudiengang(); });

    t('pruefung/anmeldungenVerwalten'); ?>

    @@ -318,6 +320,7 @@ if (empty($pruefung->result) && !$rechte->isBerechtigt('lehre/pruefungsanmeldung

    t('pruefung/pruefungPruefungenTitle'); ?>

    + diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 3a743f065..c096899ee 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -77,7 +77,6 @@ require_once('dbupdate_3.4/55614_perm_verwaltetoe.php'); require_once('dbupdate_3.4/25999_C4_dashboard.php'); require_once('dbupdate_3.4/61730_Dashboard_Anpassungen.php'); require_once('dbupdate_3.4/40128_search.php'); -require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php'); require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen diff --git a/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php b/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php index 94e1af155..822de1057 100644 --- a/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php +++ b/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php @@ -140,12 +140,17 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || let uebernahmenCheckboxen = $("input[name='lvUebernehmenCheckbox']:checked"); let checkboxenUebernahmeCount = uebernahmenCheckboxen.length; - if (!lvidFrom || !lvidTo) + let lvidautoFrom = $('#autocompleteFrom').data("lv-id"); + let lvidautoTo = $('#autocompleteTo').data("lv-id"); + + if ((!lvidFrom && !lvidautoFrom) || (!lvidTo && !lvidautoTo)) { alert("Bitte in beiden Dropdowns eine LV auswählen!"); return; } + lvidTo = lvidTo || lvidautoTo; + if (checkboxenUebernahmeCount === 0) { alert("Bitte Kompatible LV´s auswählen die übernommen werden sollen!"); @@ -159,7 +164,18 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || done++; if (done === checkboxenUebernahmeCount) { - $('#lvDropdownTo').trigger('change'); + let drodpwonval = $('#lvDropdownTo').val(); + const autocompleteval = $('#autocompleteTo').data('lv-id'); + + if (drodpwonval) + { + $('#lvDropdownTo').trigger('change'); + } + else if (autocompleteval) + { + callLoadKompatibleLvs(autocompleteval, 'To'); + + } } }); }); @@ -211,9 +227,7 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || loadKompatibleLvs(side) }) - $('#kompatibleLVs' + side).tablesorter({ - widgets: ["zebra"] - }); + $('#kompatibleLVs' + side).data('ts-initialized', false); $("#autocomplete" + side).autocomplete({ source: "lehrveranstaltung_kompatibel_vergleichen.php?autocomplete=" + side, @@ -230,6 +244,7 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || }, select: function(event, ui) { callLoadKompatibleLvs(ui.item.lehrveranstaltung_id, side) + $(this).data("lv-id", ui.item.lehrveranstaltung_id); $("#lvBezeichnung" + side).html(ui.item.bezeichnung); } }); @@ -237,6 +252,16 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || } + function sortTable(side) + { + if ($("#kompatibleLVs" + side + " tbody tr").length > 0 && !$('#kompatibleLVs' + side).data('ts-initialized')) + { + $('#kompatibleLVs' + side).tablesorter({ + widgets: ["zebra"], + }); + } + } + function loadSemester(side) { var studiengang_kz = $("#stgDropdown" + side).val(); @@ -318,6 +343,7 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || }); $("#kompatibleLVs" + side + " tbody").html(html); + sortTable(side); markDifferences(); }, error: function(jqXHR, textStatus, errorThrown) @@ -328,8 +354,14 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || } function markDifferences() { - const fromVal = $("#lvDropdownFrom").val(); - const toVal = $("#lvDropdownTo").val(); + let fromVal = $("#lvDropdownFrom").val(); + let toVal = $("#lvDropdownTo").val(); + + let lvidautoFrom = $('#autocompleteFrom').data("lv-id"); + let lvidautoTo = $('#autocompleteTo').data("lv-id"); + + fromVal = fromVal || lvidautoFrom; + toVal = toVal || lvidautoTo; if (!fromVal || fromVal === "null" || !toVal || toVal === "null") { From 05a3c40a9e4a37b20b41f93a0304b913bb945150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96sterreicher?= Date: Mon, 1 Sep 2025 15:56:33 +0200 Subject: [PATCH 116/312] Falschen Config Eintrag verschoben --- config/cis.config-default.inc.php | 3 +++ config/global.config-default.inc.php | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/cis.config-default.inc.php b/config/cis.config-default.inc.php index 4f6745ce7..9985a90d4 100644 --- a/config/cis.config-default.inc.php +++ b/config/cis.config-default.inc.php @@ -249,6 +249,9 @@ define('CIS_INFOSCREEN_NEWS_ANZEIGEN',false); //User, welcher für das Anlegen von Anrechnungen bei der Prüfungsanmeldung verwendet wird define('CIS_PRUEFUNGSANMELDUNG_USER','p.pruefungsanmeldung'); +//Gibt an, ob die Studierenden bei der Anmeldung zu einer Prüfung den Ausmas an ECTS angeben können +define('CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE', false); + // Anmeldefristen für Prüfungen in Tagen; // Wenn nicht definiert: 3 //define('CIS_PRUEFUNGSANMELDUNG_FRIST',3); diff --git a/config/global.config-default.inc.php b/config/global.config-default.inc.php index 89b02a8ce..8cfc36bfb 100644 --- a/config/global.config-default.inc.php +++ b/config/global.config-default.inc.php @@ -263,9 +263,6 @@ define('CIS_SAALPLAN_ALLERAEUME_OPTION', false); //Legt fest ob Bestätigungsmails über eine Anmelung zu einer Prüfung an eine einzelne Person erfolgt oder an den jeweiligen Lektor. (Leerstring für jeweiligen Lektor | uid); define('CIS_PRUEFUNG_MAIL_EMPFAENGER_ANMEDLUNG',""); -//Gibt an, ob die Studierenden bei der Anmeldung zu einer Prüfung den Ausmas an ECTS angeben können -define('CIS_PRUEFUNGSANMELDUNG_ECTS_ANGABE', false); - // Username fuer STIP Schnittstelle define('STIP_USER_NAME','stipendienstelle'); // Passwort fuer STIP Schnittstelle From fa6d651b2103ec5def3c8d8ed9f109d46abaabfb Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Mon, 1 Sep 2025 16:48:24 +0200 Subject: [PATCH 117/312] phrasen & permissionLib/Phrases reference $param in lvMenuBuild event --- .../controllers/api/frontend/v1/LvMenu.php | 2 + system/phrasesupdate.php | 186 +++++++++++++++++- 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/application/controllers/api/frontend/v1/LvMenu.php b/application/controllers/api/frontend/v1/LvMenu.php index 393c4d5c3..2a305dbb5 100644 --- a/application/controllers/api/frontend/v1/LvMenu.php +++ b/application/controllers/api/frontend/v1/LvMenu.php @@ -269,6 +269,8 @@ class LvMenu extends FHCAPI_Controller 'lehrfach_id'=>$lehrfach_id, 'lektor_der_lv'=>$lektor_der_lv, 'lehrfach_oe_kurzbz_arr'=>$lehrfach_oe_kurzbz_arr, + 'permissionLib' => &$this->PermissionLib, + 'phrasesLib' => &$this->PhrasesLib ]; Events::trigger('lvMenuBuild', diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index dd23a1c4a..82ea03ba0 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -33361,18 +33361,18 @@ array( array( 'app' => 'anwesenheiten', 'category' => 'global', - 'phrase' => 'anwTimeline', + 'phrase' => 'anwTimelineV2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Digitale Anwesenheiten Timeline', + 'text' => 'Digitale Anwesenheiten Timeline EXPERIMENTELL', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'Digital Attendances Timeline', + 'text' => 'Digital Attendances Timeline EXPERIMENTAL', 'description' => '', 'insertvon' => 'system' ) @@ -33850,6 +33850,186 @@ array( ) ) ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'kontrolleRestart', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kontrolle wiederholen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Restart attendance check', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipLegende', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Status Legende für das Digitales Anwesenheiten und Entschuldigungsmanagement anzeigen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Show status legend for the digital attendance and excuse note management', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipCsv', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Tabelle als CSV Datei exportiern', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Export table as CSV file', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipEdit', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anwesenheitskontrollen bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Edit attendance checks', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipSaveChanges', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Manuell veränderte Anwesenheiten speichern', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Save manually edited attendance entries', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipRestartKontrolle', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bestehende Anwesenheitskontrolle neu starten. Studierende, welche bereits gültig registriert sind, gelten weiterhin als anwesend!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Restart existing attendance check. Students who are already validly registered will continue to be considered present!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipDeleteKontrolle', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anwesenheitskontrolle löschen. Die dazugehörigen Statuseinträge der Studierenden werden auch gelöscht!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Delete attendance checks. The corresponding student status entries will also be deleted!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'tooltipEditKontrollzeiten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zeitgrenzen einer Kontrolle bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Edit time limits of an attendance check', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'anwesenheiten', + 'category' => 'global', + 'phrase' => 'anwesenheit', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anwesenheit', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Attendance', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // // DIGITALE ANWESENHEITEN PHRASEN END // From 374c1b66b5997a37b924f6931189c19409f1565d Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 2 Sep 2025 12:56:45 +0200 Subject: [PATCH 118/312] alle fehlenden lvs markieren alle abwaehlen id im title beim delete autocomplete beruecksichtigen --- ...hrveranstaltung_kompatibel_vergleichen.php | 59 ++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php b/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php index 822de1057..42604fdf6 100644 --- a/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php +++ b/vilesci/lehre/lehrveranstaltung_kompatibel_vergleichen.php @@ -165,7 +165,7 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || if (done === checkboxenUebernahmeCount) { let drodpwonval = $('#lvDropdownTo').val(); - const autocompleteval = $('#autocompleteTo').data('lv-id'); + let autocompleteval = $('#autocompleteTo').data('lv-id'); if (drodpwonval) { @@ -179,6 +179,18 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || } }); }); + }), + + $('#alleLVsMarkieren').on("click", function() + { + let checkboxen = $("#kompatibleLVsFrom tbody tr.missing-in-to").find("input[name='lvUebernehmenCheckbox']"); + checkboxen.prop("checked", true); + }), + + $('#alleLVsAbwaehlen').on("click", function() + { + let checkboxen = $("#kompatibleLVsFrom tbody").find("input[name='lvUebernehmenCheckbox']"); + checkboxen.prop("checked", false); }) }); @@ -245,7 +257,7 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || select: function(event, ui) { callLoadKompatibleLvs(ui.item.lehrveranstaltung_id, side) $(this).data("lv-id", ui.item.lehrveranstaltung_id); - $("#lvBezeichnung" + side).html(ui.item.bezeichnung); + $("#lvBezeichnung" + side).html(ui.item.bezeichnung + " (ID: " + ui.item.lehrveranstaltung_id + ")"); } }); @@ -301,7 +313,7 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || { let lv_id = $("#lvDropdown" + side).val(); - if (lv_id == "null") + if (lv_id === null) $("#lvBezeichnung" + side).html("") else { @@ -543,13 +555,42 @@ if (isset($_REQUEST['autocomplete']) && ($_REQUEST['autocomplete'] === 'From' || alert(data.errormsg); } - if ($('#lvDropdownFrom').val() === $('#lvDropdownTo').val()) + let fromValDropdown = $("#lvDropdownFrom").val(); + let toValDropdwon = $("#lvDropdownTo").val(); + + let lvidautoFrom = $('#autocompleteFrom').data("lv-id"); + let lvidautoTo = $('#autocompleteTo').data("lv-id"); + + let fromVal = fromValDropdown || lvidautoFrom; + let toVal = toValDropdwon || lvidautoTo; + + + if (fromVal === toVal) { - $('#lvDropdownFrom').trigger('change'); - $('#lvDropdownTo').trigger('change'); + if (fromValDropdown) + $('#lvDropdownFrom').trigger('change'); + else if (lvidautoFrom) + callLoadKompatibleLvs(fromVal, 'From'); + + if (toValDropdwon) + $('#lvDropdownTo').trigger('change'); + else if (lvidautoFrom) + callLoadKompatibleLvs(toVal, 'To'); } else - $('#lvDropdown' + side).trigger('change'); + { + let dropdownVal = $("#lvDropdown" + side).val(); + let autoVal = $('#autocomplete' + side).data("lv-id"); + if (dropdownVal) + { + $('#lvDropdown' + side).trigger('change'); + } + else if (autoVal) + { + callLoadKompatibleLvs(autoVal, side); + } + } + }).error(function(data) { alert(data.responseText); @@ -622,6 +663,10 @@ echo "
    Kompatible Lehrveranstaltungen - +
    +
    + +
    From 396251b0613d8fea526f8382d5702fd1b1fb44a2 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 2 Sep 2025 13:35:15 +0200 Subject: [PATCH 119/312] lvverwaltung einschraenkung bei den tags erledigte tags werden in der header suche nicht beruecksichtigt --- application/config/lvverwaltung.php | 11 ++ .../controllers/api/frontend/v1/lv/Gruppe.php | 2 +- .../controllers/api/frontend/v1/lv/Tags.php | 26 +++ application/core/Tag_Controller.php | 150 +++++++++++++++--- .../models/education/Lehreinheit_model.php | 23 ++- public/js/components/Tag/Tag.js | 10 +- .../tabulator/filters/extendedHeaderFilter.js | 3 +- 7 files changed, 193 insertions(+), 32 deletions(-) create mode 100644 application/config/lvverwaltung.php diff --git a/application/config/lvverwaltung.php b/application/config/lvverwaltung.php new file mode 100644 index 000000000..466f5c7ab --- /dev/null +++ b/application/config/lvverwaltung.php @@ -0,0 +1,11 @@ + ['readonly' => false], + 'hinweis' => ['readonly' => false], + 'hinweis_assistenz' => ['readonly' => false], + 'finished_stg' => ['readonly' => false], + + 'finished_kf' => ['readonly' => true], + 'inwork_kf' => ['readonly' => true], +]; diff --git a/application/controllers/api/frontend/v1/lv/Gruppe.php b/application/controllers/api/frontend/v1/lv/Gruppe.php index 4a1e68656..83a4fb696 100644 --- a/application/controllers/api/frontend/v1/lv/Gruppe.php +++ b/application/controllers/api/frontend/v1/lv/Gruppe.php @@ -116,7 +116,7 @@ class Gruppe extends FHCAPI_Controller bezeichnung, gid, \'false\' as lehrverband'); - $gruppen_result = $this->_ci->GruppeModel->loadWhere(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false)); + $gruppen_result = $this->_ci->GruppeModel->loadWhere(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false, 'semester IS NOT NULL' => null)); $gruppen_array = array(); diff --git a/application/controllers/api/frontend/v1/lv/Tags.php b/application/controllers/api/frontend/v1/lv/Tags.php index 234ab3cba..a22a4c82e 100644 --- a/application/controllers/api/frontend/v1/lv/Tags.php +++ b/application/controllers/api/frontend/v1/lv/Tags.php @@ -20,5 +20,31 @@ class Tags extends Tag_Controller 'doneLehre' => self::BERECHTIGUNG_KURZBZ, 'deleteLehre' => self::BERECHTIGUNG_KURZBZ, ]); + + $this->config->load('lvverwaltung'); + } + public function getTag($readonly_tags = null) + { + parent::getTag($this->config->item('lvverwaltung_tags')); + } + public function getTags($tags = null) + { + parent::getTags($this->config->item('lvverwaltung_tags')); + } + public function addTag($withZuordnung = true, $updatable_tags = null) + { + parent::addTag(true, $this->config->item('lvverwaltung_tags')); + } + public function updateTag($updatable_tags = null) + { + parent::updateTag($this->config->item('lvverwaltung_tags')); + } + public function deleteTag($withZuordnung = true, $updatable_tags = null) + { + parent::deleteTag(true, $this->config->item('lvverwaltung_tags')); + } + public function doneTag($updatable_tags = null) + { + parent::doneTag($this->config->item('lvverwaltung_tags')); } } \ No newline at end of file diff --git a/application/core/Tag_Controller.php b/application/core/Tag_Controller.php index 10e54780e..5b9bac6c5 100644 --- a/application/core/Tag_Controller.php +++ b/application/core/Tag_Controller.php @@ -29,13 +29,36 @@ class Tag_Controller extends FHCAPI_Controller $this->load->model('person/Notiz_model', 'NotizModel'); $this->load->model('system/Notiztyp_model', 'NotiztypModel'); $this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel'); + + $this->loadPhrases([ + 'ui' + ]); } - public function getTag() + public function getTag($readonly_tags = null) { $language = $this->_getLanguageIndex(); $id = $this->input->get('id'); + if (is_array($readonly_tags) && !isEmptyArray($readonly_tags)) + { + $readonly_tags = $this->_filterTag($readonly_tags, true); + + foreach ($readonly_tags as $key => $tag) + { + $readonly_tags[$key] = $this->NotizModel->db->escape($tag); + } + $tags = '(' . implode(',', $readonly_tags) . ')'; + + $this->NotizModel->addSelect(" + CASE + WHEN tbl_notiz_typ.typ_kurzbz IN $tags + THEN TRUE + ELSE FALSE + END as readonly + "); + } + $this->NotizModel->addSelect( "tbl_notiz.titel, tbl_notiz.text, @@ -54,7 +77,7 @@ class Tag_Controller extends FHCAPI_Controller $this->NotizModel->addJoin('public.tbl_benutzer verfasserbenutzer', 'tbl_notiz.verfasser_uid = verfasserbenutzer.uid', 'LEFT'); $this->NotizModel->addJoin('public.tbl_person verfasserperson', 'verfasserbenutzer.person_id = verfasserperson.person_id', 'LEFT'); - $this->NotizModel->addJoin('public.tbl_benutzer bearbeiterbenutzer', 'tbl_notiz.verfasser_uid = bearbeiterbenutzer.uid', 'LEFT'); + $this->NotizModel->addJoin('public.tbl_benutzer bearbeiterbenutzer', 'tbl_notiz.bearbeiter_uid = bearbeiterbenutzer.uid', 'LEFT'); $this->NotizModel->addJoin('public.tbl_person bearbeiterperson', 'bearbeiterbenutzer.person_id = bearbeiterperson.person_id', 'LEFT'); $notiz = $this->NotizModel->loadWhere(array('notiz_id' => $id)); @@ -62,7 +85,7 @@ class Tag_Controller extends FHCAPI_Controller $this->terminateWithSuccess(hasData($notiz) ? getData($notiz)[0] : array()); } - public function getTags() + public function getTags($tags = null) { $this->NotiztypModel->addSelect( 'typ_kurzbz as tag_typ_kurzbz, @@ -73,19 +96,36 @@ class Tag_Controller extends FHCAPI_Controller ' ); $this->NotiztypModel->addOrder('prioritaet'); + + if (is_array($tags) && !isEmptyArray($tags)) + { + $tags = $this->_filterTag($tags, false); + $this->NotiztypModel->db->where_in('typ_kurzbz', $tags); + } + $notiztypen = $this->NotiztypModel->loadWhere(array('aktiv' => true)); $this->terminateWithSuccess(hasData($notiztypen) ? getData($notiztypen) : array()); } - public function addTag($withZuordnung = true) + public function addTag($withZuordnung = true, $updatable_tags = null) { $postData = $this->getPostJson(); $checkTyp = $this->NotiztypModel->loadWhere(array('typ_kurzbz' => $postData->tag_typ_kurzbz)); - if (!hasData($checkTyp)) - $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + if (isError($checkTyp)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + if (!hasData($checkTyp)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (is_array($updatable_tags) && !isEmptyArray($updatable_tags)) + { + $tags = $this->_filterTag($updatable_tags, false); + + if (!in_array($postData->tag_typ_kurzbz, $tags)) + $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); + } if ($withZuordnung) { @@ -125,48 +165,88 @@ class Tag_Controller extends FHCAPI_Controller } } - private function addNotiz($postData) - { - return $this->NotizModel->insert(array( - 'titel' => 'TAG', //TODO klären - 'text' => $postData->notiz, - 'verfasser_uid' => $this->_uid, - 'erledigt' => false, - 'insertamum' => date('Y-m-d H:i:s'), - 'insertvon' => $this->_uid, - 'typ' => $postData->tag_typ_kurzbz - )); - - } - public function updateTag() + public function updateTag($updatable_tags = null) { $postData = $this->getPostJson(); + $post_tag = $this->NotizModel->loadWhere(array('notiz_id' => $postData->id)); + + if (isError($post_tag)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (!hasData($post_tag)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (is_array($updatable_tags) && !isEmptyArray($updatable_tags)) + { + $tags = $this->_filterTag($updatable_tags, false); + + $post_tag_typ = getData($post_tag)[0]->typ; + + if (!in_array($post_tag_typ, $tags)) + $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); + } + $updateData = $this->NotizModel->update(array('notiz_id' => $postData->id), array('text' => $postData->notiz, 'updateamum' => date('Y-m-d H:i:s'), 'updatevon' => $this->_uid, 'bearbeiter_uid' => $this->_uid, - ) + ) ); $this->terminateWithSuccess($updateData); } - public function doneTag() + public function doneTag($updatable_tags = null) { $postData = $this->getPostJson(); + $post_tag = $this->NotizModel->loadWhere(array('notiz_id' => $postData->id)); + + if (isError($post_tag)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (!hasData($post_tag)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (is_array($updatable_tags) && !isEmptyArray($updatable_tags)) + { + $tags = $this->_filterTag($updatable_tags, false); + + $post_tag_typ = getData($post_tag)[0]->typ; + + if (!in_array($post_tag_typ, $tags)) + $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); + } + $updateData = $this->NotizModel->update(array('notiz_id' => $postData->id), array('erledigt' => !$postData->done, + 'text' => $postData->notiz, 'updateamum' => date('Y-m-d H:i:s'), 'updatevon' => $this->_uid, 'bearbeiter_uid' => $this->_uid, ) ); - $this->terminateWithSuccess($updateData); } - public function deleteTag($withZuordnung = true) + public function deleteTag($withZuordnung = true, $updatable_tags = null) { $postData = $this->getPostJson(); + $post_tag = $this->NotizModel->loadWhere(array('notiz_id' => $postData->id)); + + if (isError($post_tag)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (!hasData($post_tag)) + $this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen')); + + if (is_array($updatable_tags) && !isEmptyArray($updatable_tags)) + { + $tags = $this->_filterTag($updatable_tags, false); + + $post_tag_typ = getData($post_tag)[0]->typ; + + if (!in_array($post_tag_typ, $tags)) + $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); + } $deleteNotiz = ""; if ($withZuordnung) @@ -208,5 +288,27 @@ class Tag_Controller extends FHCAPI_Controller return hasData($result) ? getData($result)[0]->index : 1; } + private function _filterTag($tags, $readonly = true) + { + $filtered_tags = array_filter($tags, function ($tag) use ($readonly) + { + return isset($tag['readonly']) && $tag['readonly'] === $readonly; + }); + + return array_keys($filtered_tags); + } + + private function addNotiz($postData) + { + return $this->NotizModel->insert(array( + 'titel' => 'TAG', //TODO klären + 'text' => $postData->notiz, + 'verfasser_uid' => $this->_uid, + 'erledigt' => false, + 'insertamum' => date('Y-m-d H:i:s'), + 'insertvon' => $this->_uid, + 'typ' => $postData->tag_typ_kurzbz + )); + } } \ No newline at end of file diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index c373f5449..9b850f0d0 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -689,10 +689,26 @@ EOSQL; private function _getTagsCTE() { + $this->load->config('lvverwaltung'); + $tags = $this->config->item('tags'); + + $whereTags = ''; + if (is_array($tags) && !isEmptyArray($tags)) + { + $tags = array_keys($tags); + + foreach ($tags as $key => $tag) + { + $tags[$key] = $this->db->escape($tag); + } + + $whereTags = " AND tbl_notiz_typ.typ_kurzbz IN (" . implode(",", $tags) . ")"; + } + return "tag_data_agg AS ( SELECT lehreinheit_id, - COALESCE(json_agg(tag ORDER BY id), '[]'::json) AS tags + COALESCE(json_agg(tag ORDER BY done), '[]'::json) AS tags FROM ( SELECT DISTINCT ON (public.tbl_notiz.notiz_id) tbl_notiz.notiz_id AS id, @@ -705,8 +721,9 @@ EOSQL; FROM public.tbl_notizzuordnung JOIN public.tbl_notiz ON tbl_notizzuordnung.notiz_id = tbl_notiz.notiz_id JOIN public.tbl_notiz_typ ON tbl_notiz.typ = tbl_notiz_typ.typ_kurzbz - WHERE lehreinheit_id IN (SELECT lehreinheit_id FROM lehreinheiten) - ) AS tag + WHERE lehreinheit_id IN (SELECT lehreinheit_id FROM lehreinheiten)" + . $whereTags. + ") AS tag GROUP BY lehreinheit_id )"; } diff --git a/public/js/components/Tag/Tag.js b/public/js/components/Tag/Tag.js index a3243ed47..a7c7a61ab 100644 --- a/public/js/components/Tag/Tag.js +++ b/public/js/components/Tag/Tag.js @@ -86,6 +86,7 @@ export default { this.tagData.updateamum = this.formatDateTime(item.updateamum) this.tagData.bearbeiter = item.bearbeiter; this.tagData.verfasser = item.verfasser; + this.tagData.readonly = item.readonly; if (item && item.notiz_id) { @@ -154,7 +155,8 @@ export default { let postData = { id: this.selectedTagId, - done: !this.tagData.done + done: !this.tagData.done, + notiz: this.tagData.notiz, } this.$api.call(this.endpoint.doneTag(postData)) this.$emit("updated", this.tagData); @@ -182,7 +184,8 @@ export default { verfasser: "", updateamum: "", bearbeiter: "", - response: "" + response: "", + readonly: false }; this.selectedTagId = null; this.mode = "create"; @@ -230,6 +233,7 @@ export default { v-model="tagData.notiz" type="textarea" field="notiz" + :readonly="tagData.readonly" placeholder="Notiz..." >
    @@ -243,7 +247,7 @@ export default {
    - - + + + From 379270c2962225c636f33fc7d375fea841d9bbf7 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 16 Sep 2025 11:57:27 +0200 Subject: [PATCH 169/312] consider person foto_sperre in advanced cis search --- application/config/searchcis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/searchcis.php b/application/config/searchcis.php index e3d1e2213..12bad025d 100644 --- a/application/config/searchcis.php +++ b/application/config/searchcis.php @@ -27,7 +27,7 @@ $config['student']['resultfields'] = [ "(p.vorname || ' ' || p.nachname) AS name", "ARRAY[s.student_uid || '@' || '" . DOMAIN . "'] AS email", "CASE - WHEN p.foto IS NOT NULL THEN 'data:image/jpeg' || CONVERT_FROM(DECODE('3b','hex'), 'UTF8') || 'base64,' || p.foto + WHEN (p.foto_sperre = false AND p.foto IS NOT NULL) THEN 'data:image/jpeg' || CONVERT_FROM(DECODE('3b','hex'), 'UTF8') || 'base64,' || p.foto ELSE NULL END AS photo_url", "b.aktiv" From cb5623d228cf4bf88297ae373e1f92660902d245 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 16 Sep 2025 13:32:28 +0200 Subject: [PATCH 170/312] label and layout for documents --- .../EditProfilComponents/EditAdresse.js | 4 +- .../Cis/ProfilUpdate/AcceptDenyUpdate.js | 13 +++--- system/phrasesupdate.php | 40 +++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js index cb702c27d..83d98a31f 100644 --- a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js +++ b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js @@ -94,7 +94,7 @@ export default { this.data[bind] = event.target.checked; } else if(bind === 'files') { if(this.dmsData.length > 0 && this.dmsData[0].type !== 'application/x.fhc-dms+json') { - this.fileschanged = true; + this.fileschanged = true; } this.updateFileID(this.dmsData); } else { @@ -241,6 +241,7 @@ export default {
    +
    {{$p.t('profilUpdate','meldebestaetigung')}}*
    +
     
    -
    - +
    +
    {{$p.t('profilUpdate','nachweisdokumente')}}
    + +
    + diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 0bfcdc466..5b4f17ced 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -48370,6 +48370,46 @@ and represent the current state of research on the topic. The prescribed citatio ) ) ), + array( + 'app' => 'core', + 'category' => 'profilUpdate', + 'phrase' => 'meldebestaetigung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Meldebestätigung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Confirmation of registration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'profilUpdate', + 'phrase' => 'nachweisdokumente', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nachweisdokumente', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Confirmation documents', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), ); From 34eb37306120f2eeec7c1d5e9651ec6970675620 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 16 Sep 2025 13:39:15 +0200 Subject: [PATCH 171/312] adapt phrase profilUpdateInformationMessage --- system/phrasesupdate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 5b4f17ced..43e9c0fcc 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -29149,7 +29149,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Erneuern Sie ihr {0} und laden Sie die passenden Beweisdokumente hoch', + 'text' => 'Aktualisieren Sie ihren {0} und laden Sie die passenden Nachweisdokumente hoch', 'description' => '', 'insertvon' => 'system' ), From cef581178cd18aefc5907d7cb74cc210a9faac8c Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 16 Sep 2025 13:52:52 +0200 Subject: [PATCH 172/312] show documents in status view --- .../Profil/ProfilModal/EditProfilComponents/Status.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/Status.js b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/Status.js index a0492a2da..d8c7637b3 100644 --- a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/Status.js +++ b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/Status.js @@ -112,14 +112,17 @@ export default {
    {{topic}}
    {{data.value}} - + +
    +
    {{$p.t('profilUpdate','nachweisdokumente')}}
    + +
    `, }; From 7e40e34212f30191121dec324eff8f1e83ca9655 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 16 Sep 2025 17:40:27 +0200 Subject: [PATCH 173/312] remove DMS_PATH from original user filename --- application/libraries/DmsLib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/DmsLib.php b/application/libraries/DmsLib.php index 774ebdc79..c6c16a866 100644 --- a/application/libraries/DmsLib.php +++ b/application/libraries/DmsLib.php @@ -670,7 +670,7 @@ class DmsLib $fileObj = new stdClass(); $fileObj->filename = getData($result)[0]->filename; $fileObj->file = DMS_PATH.getData($result)[0]->filename; - $fileObj->name = DMS_PATH.getData($result)[0]->name; // original user filename + $fileObj->name = getData($result)[0]->name; // original user filename $fileObj->mimetype = getData($result)[0]->mimetype; return success($fileObj); From aca4195df637f363be2fa7a995634d2921d0fdf4 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 17 Sep 2025 11:01:08 +0200 Subject: [PATCH 174/312] stv kontaktieren tab hinzugefuegt --- .../api/frontend/v1/stv/Config.php | 5 + .../Details/Kontaktieren.js | 101 +++++++++++++++ system/phrasesupdate.php | 120 ++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 2fb436384..da3c67b4a 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -227,6 +227,11 @@ class Config extends FHCAPI_Controller ] ]; + $result['kontaktieren'] = [ + 'title' => $this->p->t('stv', 'tab_kontaktieren'), + 'component' => './Stv/Studentenverwaltung/Details/Kontaktieren.js', + ]; + Events::trigger('stv_conf_students', function & () use (&$result) { return $result; }); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js b/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js new file mode 100644 index 000000000..bd7554a47 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js @@ -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: ` +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    ` +}; \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 43e9c0fcc..5893efc61 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -43075,6 +43075,126 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'tab_kontaktieren', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kontaktieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Get in Contact', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'internEMail', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'E-Mail senden (intern)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Send email (internal)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'privateEMail', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'E-Mail senden (privat)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Send email (private)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'bccEMail', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'STRG-Taste für BCC', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'CTRL-Key for BCC', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'weitereEMail', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ein weiteres Fenster wird geöffnet. Fortfahren?', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'A new window will open. Continue?', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'zuvieleEMails', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Aufgrund der hohen Empfängerzahl muss die Nachricht auf mehrere E-Mails aufgeteilt werden.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Due to the large number of recipients, the message needs to be split across multiple emails.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abschlusspruefung', From dff8223d3e8c593c03c0e7daf31804b93f4eaf71 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 17 Sep 2025 11:04:10 +0200 Subject: [PATCH 175/312] sortierung der tags --- public/js/components/LVVerwaltung/Setup/Table.js | 11 +++++++++++ public/js/components/filter/Filter.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index 5360c3525..fa288f8d7 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -233,6 +233,17 @@ export default { const renderTags = () => { container.innerHTML = ''; parsedTags = parsedTags.filter(item => item !== null); + + parsedTags.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bbone = b.done ? 1 : 0; + + if (adone !== bbone) + { + return adone - bbone; + } + return b.id - a.id; + }); const tagsToShow = rowData._tagExpanded ? parsedTags : parsedTags.slice(0, maxVisibleTags); tagsToShow.forEach(tag => { diff --git a/public/js/components/filter/Filter.js b/public/js/components/filter/Filter.js index cefa99404..9a294d2d4 100644 --- a/public/js/components/filter/Filter.js +++ b/public/js/components/filter/Filter.js @@ -666,7 +666,7 @@ export const CoreFilterCmpt = { {{ selectedData.length }} ausgewählt - Mit {{ selectedData.length }} ausgewählten: + Mit {{ selectedData.length }} ausgewählten: From bbfaf447f569afa7ca8c9d54648cbcf8e09b0b50 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Wed, 17 Sep 2025 12:13:15 +0200 Subject: [PATCH 176/312] remove dependency to library from pv extension and do not delete salary history when gb is deleted since foreign key is configured on delete set null --- .../libraries/vertragsbestandteil/GehaltsbestandteilLib.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/application/libraries/vertragsbestandteil/GehaltsbestandteilLib.php b/application/libraries/vertragsbestandteil/GehaltsbestandteilLib.php index 1aaafa471..c1238c13d 100644 --- a/application/libraries/vertragsbestandteil/GehaltsbestandteilLib.php +++ b/application/libraries/vertragsbestandteil/GehaltsbestandteilLib.php @@ -24,7 +24,6 @@ class GehaltsbestandteilLib $this->CI = get_instance(); $this->CI->load->model('vertragsbestandteil/Gehaltsbestandteil_model', 'GehaltsbestandteilModel'); - $this->CI->load->library('extensions/FHC-Core-Personalverwaltung/abrechnung/GehaltsLib'); $this->GehaltsbestandteilModel = $this->CI->GehaltsbestandteilModel; } @@ -121,10 +120,6 @@ class GehaltsbestandteilLib { $this->setUIDtoPGSQL(); - // delete Gehaltsabrechnung - $ret = $this->CI->gehaltslib->deleteAbrechnung($gehaltsbestandteil); - - // $ret = $this->GehaltsbestandteilModel->delete($gehaltsbestandteil->getGehaltsbestandteil_id()); if (isError($ret)) From 57531d956d1a0ef18e98ec4a50a4d5c1c3ed55a6 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 18 Sep 2025 13:18:44 +0200 Subject: [PATCH 177/312] gruppen in eigenen tab verschoben styling phrases --- .../api/frontend/v1/lv/Lehreinheit.php | 4 +- .../controllers/api/frontend/v1/lv/Lektor.php | 33 +++- .../controllers/api/frontend/v1/lv/Setup.php | 5 + application/libraries/LektorLib.php | 14 +- public/css/Lvverwaltung.css | 10 ++ public/css/Tabulator6.css | 154 +++++++++++++++- .../components/LVVerwaltung/Details/Form.js | 166 ++++++++++-------- .../LVVerwaltung/Details/Gruppen.js | 4 +- .../components/LVVerwaltung/LVVerwaltung.js | 4 +- .../components/LVVerwaltung/Lektor/Daten.js | 19 +- .../components/LVVerwaltung/Lektor/Table.js | 2 +- .../components/LVVerwaltung/Lektor/Vertrag.js | 9 +- .../js/components/LVVerwaltung/Setup/Table.js | 22 +-- .../js/components/LVVerwaltung/Setup/Tabs.js | 1 - .../components/LVVerwaltung/Tabs/Details.js | 19 +- .../components/LVVerwaltung/Tabs/Gruppen.js | 39 ++++ system/phrasesupdate.php | 40 +++++ 17 files changed, 418 insertions(+), 127 deletions(-) create mode 100644 public/js/components/LVVerwaltung/Tabs/Gruppen.js diff --git a/application/controllers/api/frontend/v1/lv/Lehreinheit.php b/application/controllers/api/frontend/v1/lv/Lehreinheit.php index b851f8c22..958007901 100644 --- a/application/controllers/api/frontend/v1/lv/Lehreinheit.php +++ b/application/controllers/api/frontend/v1/lv/Lehreinheit.php @@ -350,7 +350,7 @@ class Lehreinheit extends FHCAPI_Controller $this->form_validation->set_rules($field, 'Start KW', 'integer|greater_than[0]|less_than_equal_to[53]'); break; case 'gewicht': - $this->form_validation->set_rules($field, 'Gewicht', 'numeric'); + $this->form_validation->set_rules($field, 'Gewicht', 'numeric|greater_than_equal_to[0]'); break; case 'raumtyp': $this->form_validation->set_rules($field, 'Raumtyp', 'required|max_length[16]'); @@ -365,7 +365,7 @@ class Lehreinheit extends FHCAPI_Controller $this->form_validation->set_rules($field, 'LVNR', 'integer'); break; case 'unr': - $this->form_validation->set_rules($field, 'UNR', 'integer'); + $this->form_validation->set_rules($field, 'UNR', 'integer|greater_than_equal_to[0]'); break; case 'lehre': $this->form_validation->set_rules($field, 'Lehre', 'trim'); diff --git a/application/controllers/api/frontend/v1/lv/Lektor.php b/application/controllers/api/frontend/v1/lv/Lektor.php index 4a244c1a1..68bec1bc4 100644 --- a/application/controllers/api/frontend/v1/lv/Lektor.php +++ b/application/controllers/api/frontend/v1/lv/Lektor.php @@ -107,7 +107,8 @@ class Lektor extends FHCAPI_Controller $this->form_validation->set_rules($field, 'Planstunden', 'integer|greater_than_equal_to[0]'); break; case 'stundensatz': - $this->form_validation->set_rules($field, 'Stundensatz', 'numeric|greater_than_equal_to[0]'); + $formData['stundensatz'] = str_replace(',', '.', $formData['stundensatz']); + $this->form_validation->set_rules($field, 'Stundensatz', 'callback__check_stundensatz'); break; case 'faktor': $this->form_validation->set_rules($field, 'Faktor', 'numeric|greater_than_equal_to[0]'); @@ -119,6 +120,7 @@ class Lektor extends FHCAPI_Controller $this->form_validation->set_rules($field, 'Bis Melden', 'trim'); break; case 'semesterstunden': + $formData['semesterstunden'] = str_replace(',', '.', $formData['semesterstunden']); $this->form_validation->set_rules($field, 'Semesterstunden', 'callback__check_semesterstunden'); break; case 'mitarbeiter_uid': @@ -129,7 +131,7 @@ class Lektor extends FHCAPI_Controller } if (!$this->form_validation->run()) { - $this->terminateWithError($this->form_validation->error_array()); + $this->terminateWithValidationErrors($this->form_validation->error_array()); } if (isset($formData['semesterstunden']) && (!is_numeric($formData['semesterstunden']) || $formData['semesterstunden'] === '')) @@ -145,9 +147,26 @@ class Lektor extends FHCAPI_Controller $result = $this->_ci->lektorlib->updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $formData); if (isError($result)) $this->terminateWithError(getError($result)); - $this->terminateWithSuccess("Erfolgreich geupdated"); + $this->terminateWithSuccess($result); } + public function _check_stundensatz($value) + { + $value = str_replace(',', '.', $value); + + if (!is_numeric($value)) + { + $this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss eine Zahl sein.'); + return false; + } + + if ($value < 0 || $value >= 10000) { + $this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss zwischen 0 und 10000 liegen.'); + return false; + } + + return true; + } public function _check_semesterstunden($value) { if ($value === null || $value === '') { @@ -171,6 +190,14 @@ class Lektor extends FHCAPI_Controller ); return false; } + if ($value > 999.99) + { + $this->form_validation->set_message( + '_check_semesterstunden', + 'Das Feld {field} darf maximal 999,99 betragen.' + ); + return false; + } return true; } diff --git a/application/controllers/api/frontend/v1/lv/Setup.php b/application/controllers/api/frontend/v1/lv/Setup.php index cf73697a7..a2c167dd6 100644 --- a/application/controllers/api/frontend/v1/lv/Setup.php +++ b/application/controllers/api/frontend/v1/lv/Setup.php @@ -50,6 +50,11 @@ class Setup extends FHCAPI_Controller 'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Details.js', 'config' => [] ); + $tabs['gruppen'] = array ( + 'title' => 'Gruppen', + 'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Gruppen.js', + 'config' => [] + ); $tabs['lektor'] = array ( 'title' => 'LektorInnenzuteilung', 'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Lektor.js', diff --git a/application/libraries/LektorLib.php b/application/libraries/LektorLib.php index 44c71cdaa..e06e2b728 100644 --- a/application/libraries/LektorLib.php +++ b/application/libraries/LektorLib.php @@ -176,11 +176,11 @@ class LektorLib if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin'))) { if (!$this->LehrauftragAufFirma(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid)) - return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\n Daten wurden NICHT gespeichert!\n\n"); + return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n"); } else { - $warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\n Daten wurden gespeichert!\n\n"; + $warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n"; } $stunden_limit_result = $this->getStundenInstitut($mitarbeiter_uid, $lehreinheit->studiensemester_kurzbz, $oe_array); @@ -190,7 +190,7 @@ class LektorLib $stunden_limit_array = getData($stunden_limit_result); foreach ($stunden_limit_array as $stunden_limit) { - $warning .= $stunden_limit->summe . ' Stunden' . $stunden_limit->bezeichnung; + $warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n"; } } } @@ -206,7 +206,7 @@ class LektorLib $benutzer_aktiv = getData($benutzer_result)[0]->aktiv; if (!$benutzer_aktiv) - $warning .= "Achtung: Der/Die Benutzer*in ist inaktiv!\nBitte informieren Sie die Personalbteilung.\n\nDaten wurden gespeichert.\n\n"; + $warning .= "Achtung: Der/Die Benutzer*in ist inaktiv!\nBitte informieren Sie die Personalbteilung.\nDaten wurden gespeichert.\n\n"; $updatableFields = array( 'semesterstunden', @@ -236,9 +236,9 @@ class LektorLib if (isError($result)) return $result; - if ($warning !== '') return error($warning); + if ($warning !== '') return success(['warning' => $warning]); - return success('Successfully updated Lehreinheit'); + return success('Erfolgreich geupdated'); } private function getMaxStunden($mitarbeiter_uid, $studiensemester_kurzbz, $studiengang_kz, $echter_dv) @@ -280,7 +280,7 @@ class LektorLib foreach ($stunden_limit_array as $stunden_limit) { - $error .= $stunden_limit->summe . ' Stunden' . $stunden_limit->bezeichnung; + $error .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n"; } } return error($error); diff --git a/public/css/Lvverwaltung.css b/public/css/Lvverwaltung.css index 38d74def4..6e0f4c760 100644 --- a/public/css/Lvverwaltung.css +++ b/public/css/Lvverwaltung.css @@ -28,3 +28,13 @@ textarea[name="anmerkung"] { content: '\f073'; color: #f3c541; } + + +.modal-dialog.modal-xxl { + max-width: 95% !important; +} + +.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after +{ + border-color: black; +} diff --git a/public/css/Tabulator6.css b/public/css/Tabulator6.css index 8be726544..7a66dd2f6 100644 --- a/public/css/Tabulator6.css +++ b/public/css/Tabulator6.css @@ -1 +1,153 @@ -@import '../../vendor/olifolkerd/tabulator6/dist/css/tabulator_simple.min.css'; \ No newline at end of file +@import '../../vendor/olifolkerd/tabulator6/dist/css/tabulator_simple.min.css'; +.tabulator-row { + border-bottom: none; +} +.tabulator-row .tabulator-frozen, +.tabulator-row .tabulator-cell { + border-bottom: 1px solid #dee2e6; +} +.tabulator-row.tabulator-row-even { + background-color: transparent; +} +.tabulator-headers .tabulator-frozen, +.tabulator-row.tabulator-row-odd .tabulator-frozen, +.tabulator-row.tabulator-row-odd .tabulator-cell { + background-color: #fff; +} +.tabulator-row.tabulator-row-even .tabulator-frozen, +.tabulator-row.tabulator-row-even .tabulator-cell { + background-color: #f2f2f2; +} +.tabulator-row.tabulator-selectable:hover .tabulator-frozen, +.tabulator-row.tabulator-selectable:hover .tabulator-cell { + background-color: #ececec; +} +.tabulator-row.tabulator-selected .tabulator-frozen, +.tabulator-row.tabulator-selected .tabulator-cell { + background-color: #9abcea; +} +.tabulator-row.tabulator-selected:hover .tabulator-frozen, +.tabulator-row.tabulator-selected:hover .tabulator-cell { + background-color: #769bcc; +} +.tabulator .tabulator-col-resize-handle:last-of-type { + z-index: 999999; +} + +/* classes for rows that are not selectable in the tabulator, except for rows that are used for calculation */ +.tabulator-row.tabulator-unselectable:not(.tabulator-calcs) { + color: #adb5bd !important; + pointer-events: none !important; +} + +.tabulator-row.tabulator-unselectable a { + pointer-events: auto !important; +} + +/* using bootstrap background classes to style the background color of tabulator rows */ +/* bg-warning */ +/* odd-rows */ +.tabulator-headers .tabulator-frozen.bg-warning, +.tabulator-row.tabulator-row-odd .tabulator-frozen.bg-warning, +.tabulator-row.tabulator-row-odd .tabulator-cell.bg-warning { + background-color: #fcf8e3; + +} +/* even-rows */ +.tabulator-headers .tabulator-frozen.bg-warning, +.tabulator-row.tabulator-row-even .tabulator-frozen.bg-warning, +.tabulator-row.tabulator-row-even .tabulator-cell.bg-warning { + background-color: #fcf8e3; + +} + +/* bg-success */ +/* odd-rows */ +.tabulator-headers .tabulator-frozen.bg-success, +.tabulator-row.tabulator-row-odd .tabulator-frozen.bg-success, +.tabulator-row.tabulator-row-odd .tabulator-cell.bg-success { + background-color: #dff0d8; + +} +/* even-rows */ +.tabulator-headers .tabulator-frozen.bg-success, +.tabulator-row.tabulator-row-even .tabulator-frozen.bg-success, +.tabulator-row.tabulator-row-even .tabulator-cell.bg-success { + background-color: #dff0d8; + +} + +/* bg-info */ +/* odd-rows */ +.tabulator-headers .tabulator-frozen.bg-info, +.tabulator-row.tabulator-row-odd .tabulator-frozen.bg-info, +.tabulator-row.tabulator-row-odd .tabulator-cell.bg-info { + background-color: #d9edf7; + +} +/* even-rows */ +.tabulator-headers .tabulator-frozen.bg-info, +.tabulator-row.tabulator-row-even .tabulator-frozen.bg-info, +.tabulator-row.tabulator-row-even .tabulator-cell.bg-info { + background-color: #d9edf7; + +} + +/* special bootstrap5 styling for tableWidget and their accordion-item ::after content */ +.accordion-button::after{ + content:none !important; +} + + +.tabulator { + font-size: 1rem; +} + +.tabulator-initialfontsize .tabulator { + font-size: 14px; +} + +.tabulator-row.tabulator-unselectable .tabulator-cell { + pointer-events: all; +} + +.tabulator-tooltip { + color: #fff; + background-color: #000; +} + +.tabulator-row.tabulator-unselectable:not(.tabulator-calcs) { + color: #777 !important; +} + +.tabulator-cell .btn { + padding: 0 .375rem; + font-size: calc(1rem - 2px / 1.5); /* substract border (2 x 1px) modified by the line-height (1.5) */ + border-radius: .2rem; + vertical-align: baseline; +} + +.tabulator-row.tabulator-selectable:focus { + box-shadow: 0 0 0 .24rem rgba(13,110,253,.25); + z-index: 1; + outline: 0; +} + +.btn-select-col-selected +{ + background-color: #e6e6e6; +} + +.accordion-item-dark, .accordion-item-dark:focus{ + background-color:#CED4DA !important; + border-color:#ADB5BD !important; + box-shadow: none !important; +} + +/** + * Make keyboard-focused list items look like the mouse-hovered list items + */ +.tabulator-edit-list .tabulator-edit-list-item.focused { + color: #fff; + background: #1d68cd; +} diff --git a/public/js/components/LVVerwaltung/Details/Form.js b/public/js/components/LVVerwaltung/Details/Form.js index eed65f7ad..5908512d7 100644 --- a/public/js/components/LVVerwaltung/Details/Form.js +++ b/public/js/components/LVVerwaltung/Details/Form.js @@ -38,33 +38,6 @@ export default { }, template: `
    -
    - - - -
    -
    - -
    + - - - -
    - -
    - - - -
    @@ -179,30 +112,109 @@ export default { {{ raumtyp.raumtyp_kurzbz }} {{ raumtyp.beschreibung }} + + +
    + + +
    + + + + + + + + + +
    + +
    + +
    + +
    + +
    + + +
    ` diff --git a/public/js/components/LVVerwaltung/Details/Gruppen.js b/public/js/components/LVVerwaltung/Details/Gruppen.js index 219b956d8..a6f9df005 100644 --- a/public/js/components/LVVerwaltung/Details/Gruppen.js +++ b/public/js/components/LVVerwaltung/Details/Gruppen.js @@ -50,7 +50,7 @@ export default{ let button = document.createElement('button'); container.className = "d-flex gap-1"; - button.className = 'btn btn-outline-secondary btn-action'; + button.className = 'btn btn-outline-secondary'; button.innerHTML = ''; button.title = this.$p.t('ui', 'loeschen'); button.addEventListener('click', (event) => { @@ -63,7 +63,7 @@ export default{ { button = document.createElement('button'); container.className = "d-flex gap-2"; - button.className = 'btn btn-outline-secondary btn-action'; + button.className = 'btn btn-outline-secondary'; button.innerHTML = ''; button.title = this.$p.t('lehre', 'auslvplanentfernen'); button.disabled = !cell.getData().verplant; diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index e9d132848..a1ff28a2e 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -88,10 +88,12 @@ export default { }, selectedStudiengang: '', searchbaroptions: { + origin: 'lvverwaltung', cssclass: "position-relative", calcheightonly: true, types: [ - "mitarbeiter" + "mitarbeiter", + "mitarbeiter_ohne_zuordnung" ], actions: { employee: { diff --git a/public/js/components/LVVerwaltung/Lektor/Daten.js b/public/js/components/LVVerwaltung/Lektor/Daten.js index 0f6b9e0f7..d4c6aa5e3 100644 --- a/public/js/components/LVVerwaltung/Lektor/Daten.js +++ b/public/js/components/LVVerwaltung/Lektor/Daten.js @@ -121,9 +121,9 @@ export default{ } this.$refs.form.call(ApiLektor.update(updatedData)) .then(result => { - let error = result.data?.error; - if (error) - this.$fhcAlert.alertWarning(error) + let warning = result.data?.retval?.warning; + if (warning) + this.$fhcAlert.alertWarning(warning) this.original = {...this.data}; if (this.changed.mitarbeiter_uid) @@ -192,6 +192,7 @@ export default{ container-class="col-3" dropdown @complete="searchLektor" + name="lektorautocomplete" > { event.stopPropagation(); this.deleteLVPlan(cell.getData().mitarbeiter_uid, cell.getData().lehreinheit_id) diff --git a/public/js/components/LVVerwaltung/Lektor/Vertrag.js b/public/js/components/LVVerwaltung/Lektor/Vertrag.js index f36fad8e0..bdb156546 100644 --- a/public/js/components/LVVerwaltung/Lektor/Vertrag.js +++ b/public/js/components/LVVerwaltung/Lektor/Vertrag.js @@ -88,8 +88,14 @@ export default{ .catch(this.$fhcAlert.handleSystemError); }, - cancelVertrag() + async cancelVertrag() { + + if (await this.$fhcAlert.confirm({ + message: this.$p.t('lehre', 'vertragConfirm'), + acceptLabel: this.$p.t('ui', 'ja').charAt(0).toUpperCase() + this.$p.t('ui', 'ja').slice(1), + acceptClass: 'btn btn-danger'}) === false) + return; let needUpdate = { vertrag_id: this.data.vertrag.vertrag_id, mitarbeiter_uid: this.mitarbeiter_uid, @@ -126,6 +132,7 @@ export default{ type="button" class="btn btn-outline-secondary w-100" @click="cancelVertrag" + :title="$p.t('lehre', 'cancelvertrag')" > diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index fa288f8d7..2f37e4207 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -670,19 +670,19 @@ export default { - - + + - + - - + + ` }; \ No newline at end of file diff --git a/public/js/components/LVVerwaltung/Setup/Tabs.js b/public/js/components/LVVerwaltung/Setup/Tabs.js index af14fbf32..62c5bbc31 100644 --- a/public/js/components/LVVerwaltung/Setup/Tabs.js +++ b/public/js/components/LVVerwaltung/Setup/Tabs.js @@ -50,7 +50,6 @@ export default { :modelValue="lv[0]" :config="configLVTabs" :default="$route.params.tab" - style="flex: 1 1 0%; height: 0%" @changed="reload" /> diff --git a/public/js/components/LVVerwaltung/Tabs/Details.js b/public/js/components/LVVerwaltung/Tabs/Details.js index ce0102177..cb3275d23 100644 --- a/public/js/components/LVVerwaltung/Tabs/Details.js +++ b/public/js/components/LVVerwaltung/Tabs/Details.js @@ -111,23 +111,14 @@ export default {
    - {{this.$p.t('lehre', 'lehreinheit')}} +
    + {{ this.$p.t('lehre', 'lehreinheit') }} + +
    -
    -
    -
    - {{this.$p.t('lehre', 'gruppen')}} - -
    -
    - {{this.$p.t('lehre', 'assignedPersons')}} - -
    -
    - -
    ` + ` }; \ No newline at end of file diff --git a/public/js/components/LVVerwaltung/Tabs/Gruppen.js b/public/js/components/LVVerwaltung/Tabs/Gruppen.js new file mode 100644 index 000000000..2879c1959 --- /dev/null +++ b/public/js/components/LVVerwaltung/Tabs/Gruppen.js @@ -0,0 +1,39 @@ +import GruppenTable from '../Details/Gruppen.js'; +import GruppenDirektTable from '../Details/Direktinskription.js'; + +export default { + name: "LVTabGruppen", + components: { + GruppenTable, + GruppenDirektTable, + }, + props: { + modelValue: Object, + config: { + type: Object, + default: {} + }, + }, + inject: { + dropdowns: { + from: 'dropdowns' + } + }, + + template: ` + +
    +
    +
    + {{this.$p.t('lehre', 'gruppen')}} + +
    +
    + {{this.$p.t('lehre', 'assignedPersons')}} + +
    +
    + +
    + ` +}; \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 0927ea309..6a4c98fd9 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -2984,6 +2984,46 @@ $phrases = array( ) ) ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'cancelvertrag', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Vertrag stornieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Cancel Contract', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'vertragConfirm', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Möchten Sie den Vertrag wirklich stornieren?', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Do you really want to cancel the contract?', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'lehre', From 41dbd83bdb39cc1035e9a5bcd7d88b7910ffab19 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 18 Sep 2025 15:27:04 +0200 Subject: [PATCH 178/312] phrases tags als default entfernt master gemerged --- application/config/lvverwaltung.php | 14 ++--- system/phrasesupdate.php | 80 +++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/application/config/lvverwaltung.php b/application/config/lvverwaltung.php index 466f5c7ab..4fe09bff3 100644 --- a/application/config/lvverwaltung.php +++ b/application/config/lvverwaltung.php @@ -1,11 +1,11 @@ ['readonly' => false], - 'hinweis' => ['readonly' => false], - 'hinweis_assistenz' => ['readonly' => false], - 'finished_stg' => ['readonly' => false], +/*It defines which tags are available in LVVerwaltung and whether they are editable - 'finished_kf' => ['readonly' => true], - 'inwork_kf' => ['readonly' => true], +$config['lvverwaltung_tags'] = [ + 'tag_1' => ['readonly' => false], + 'tag_1' => ['readonly' => true] ]; +*/ + +$config['lvverwaltung_tags'] = []; \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 437e76c1e..008ad8a8d 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -43278,6 +43278,86 @@ array( ) ) ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'lehrauftrag', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrauftrag', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Teaching lectureship', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'kategorie', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kategorie', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Category', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'project', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kategorie', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Category', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'lventwicklung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LV-Weiterentwicklung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course Development', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'pep', 'category' => 'ui', From 566938d4f053b78e1748a208fb509a0c6bf2f5a1 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Thu, 18 Sep 2025 17:25:00 +0200 Subject: [PATCH 179/312] Studierendenverwaltung Abschlusspruefung: moved Vorsitz and Pruefer search to backend, searched by vorname or nachname or combinations of vorname and nachname --- .../api/frontend/v1/stv/Abschlusspruefung.php | 132 ++++++--------- .../models/ressource/Mitarbeiter_model.php | 14 +- .../js/api/factory/stv/abschlusspruefung.js | 19 +-- .../Abschlusspruefung/Abschlusspruefung.js | 152 ++++++++++++------ 4 files changed, 162 insertions(+), 155 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php index d8bc067f9..def4f6502 100644 --- a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php +++ b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php @@ -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); - } } diff --git a/application/models/ressource/Mitarbeiter_model.php b/application/models/ressource/Mitarbeiter_model.php index bf4672070..0dad7443e 100644 --- a/application/models/ressource/Mitarbeiter_model.php +++ b/application/models/ressource/Mitarbeiter_model.php @@ -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 .= ", p.person_id, 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); } diff --git a/public/js/api/factory/stv/abschlusspruefung.js b/public/js/api/factory/stv/abschlusspruefung.js index db8403b48..dcba61887 100644 --- a/public/js/api/factory/stv/abschlusspruefung.js +++ b/public/js/api/factory/stv/abschlusspruefung.js @@ -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/' - }; - } -}; \ No newline at end of file +}; diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js index e5c4cade1..b8f4ee11b 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js @@ -260,14 +260,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 +309,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 +457,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 +549,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 +666,8 @@ export default { optionValue="mitarbeiter_uid" dropdown forceSelection - :suggestions="listeFilteredMitarbeiter" - @complete="filterMitarbeiter" + :suggestions="filteredMitarbeiter" + @complete="searchMitarbeiter" :min-length="3" >
    @@ -636,9 +679,10 @@ export default { v-model="selectedPruefer1" optionLabel="label" optionValue="person_id" + dropdown forceSelection - :suggestions="listeFilteredPersons" - @complete="filterPersons" + :suggestions="filteredPersons" + @complete="searchPerson" :min-length="3" >
    @@ -669,9 +713,10 @@ export default { v-model="selectedPruefer2" optionLabel="label" optionValue="person_id" + dropdown forceSelection - :suggestions="listeFilteredPersons" - @complete="filterPersons" + :suggestions="filteredPersons" + @complete="searchPerson" :min-length="3" > @@ -701,9 +746,10 @@ export default { v-model="selectedPruefer3" optionLabel="label" optionValue="person_id" + dropdown forceSelection - :suggestions="listeFilteredPersons" - @complete="filterPersons" + :suggestions="filteredPersons" + @complete="searchPerson" :min-length="3" > From 0577f3f8b96a31fbd7f087c1586a2de717d64b30 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Thu, 18 Sep 2025 18:46:11 +0200 Subject: [PATCH 180/312] Cis4 Profile only show current userfunctions with description from tbl_funktion --- application/libraries/ProfilLib.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/application/libraries/ProfilLib.php b/application/libraries/ProfilLib.php index 2a130d263..e86a343e7 100644 --- a/application/libraries/ProfilLib.php +++ b/application/libraries/ProfilLib.php @@ -303,10 +303,22 @@ class ProfilLib{ private function getBenutzerFunktion($uid) { $this->ci->load->model("person/Benutzerfunktion_model","BenutzerfunktionModel"); - $this->ci->BenutzerfunktionModel->addSelect(["tbl_benutzerfunktion.bezeichnung as Bezeichnung", "tbl_organisationseinheit.bezeichnung as Organisationseinheit", "datum_von as Gültig_von", "datum_bis as Gültig_bis", "wochenstunden as Wochenstunden"]); + $this->ci->BenutzerfunktionModel->addSelect([ + "tbl_funktion.beschreibung as Bezeichnung", + "tbl_organisationseinheit.bezeichnung as Organisationseinheit", + "datum_von as Gültig_von", + "datum_bis as Gültig_bis", + "COALESCE(wochenstunden, '0'::numeric(5,2)) AS \"Wochenstunden\"" + ]); + $this->ci->BenutzerfunktionModel->addJoin("tbl_funktion", "funktion_kurzbz"); $this->ci->BenutzerfunktionModel->addJoin("tbl_organisationseinheit", "oe_kurzbz"); - $benutzer_funktion_res = $this->ci->BenutzerfunktionModel->loadWhere(array('uid' => $uid)); + $benutzer_funktion_res = $this->ci->BenutzerfunktionModel->loadWhere( + array( + 'uid' => $uid, + 'NOW()::date BETWEEN COALESCE(datum_von, \'1970-01-01\'::date) AND COALESCE(datum_bis, \'2170-12-01\'::date)' => null + ) + ); if(isError($benutzer_funktion_res)){ return error(getData($benutzer_funktion_res)); } From 41143a6dfc66bf34bb6b454ae2c6e059de015fc8 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Fri, 19 Sep 2025 10:58:53 +0200 Subject: [PATCH 181/312] profile functions use tbl_benutzerfunktion.beschreibung in column description, format date columns in profile tabulators, fix vuejs dev version warnings --- application/libraries/ProfilLib.php | 2 +- .../Cis/Profil/MitarbeiterProfil.js | 35 ++++++++++++++----- .../Cis/Profil/MitarbeiterViewProfil.js | 27 +++++++++++--- .../ProfilComponents/RoleInformation.js | 19 ++++++++-- .../EditProfilComponents/ImageUpload.js | 4 +-- .../js/components/Cis/Profil/StudentProfil.js | 21 ++++++++--- 6 files changed, 85 insertions(+), 23 deletions(-) diff --git a/application/libraries/ProfilLib.php b/application/libraries/ProfilLib.php index e86a343e7..50b8bc264 100644 --- a/application/libraries/ProfilLib.php +++ b/application/libraries/ProfilLib.php @@ -304,7 +304,7 @@ class ProfilLib{ { $this->ci->load->model("person/Benutzerfunktion_model","BenutzerfunktionModel"); $this->ci->BenutzerfunktionModel->addSelect([ - "tbl_funktion.beschreibung as Bezeichnung", + "CASE WHEN (tbl_benutzerfunktion.bezeichnung IS NOT NULL AND tbl_benutzerfunktion.bezeichnung <> '' AND tbl_benutzerfunktion.bezeichnung <> tbl_funktion.beschreibung) THEN tbl_funktion.beschreibung || ' - ' || tbl_benutzerfunktion.bezeichnung ELSE tbl_funktion.beschreibung END as \"Bezeichnung\"", "tbl_organisationseinheit.bezeichnung as Organisationseinheit", "datum_von as Gültig_von", "datum_bis as Gültig_bis", diff --git a/public/js/components/Cis/Profil/MitarbeiterProfil.js b/public/js/components/Cis/Profil/MitarbeiterProfil.js index 223b5c2b6..bf3c4cf49 100644 --- a/public/js/components/Cis/Profil/MitarbeiterProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterProfil.js @@ -11,6 +11,7 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js"; import ProfilInformation from "./ProfilComponents/ProfilInformation.js"; import ApiProfilUpdate from '../../../api/factory/profilUpdate.js'; +import { dateFilter } from '../../../tabulator/filters/Dates.js'; export default { components: { @@ -40,7 +41,7 @@ export default { persistence: { columns: false }, - height: 300, + minHeight: 300, layout: "fitColumns", responsiveLayout: "collapse", responsiveLayoutCollapseUseFormatters: false, @@ -74,18 +75,24 @@ export default { { title: Vue.computed(() => this.preloadedPhrasen.gueltigVonPhrase), field: "Gültig_von", - headerFilter: true, + headerFilterFunc: 'dates', + headerFilter: dateFilter, resizable: true, minWidth: 200, - visible: true + visible: true, + formatter:"datetime", + formatterParams: this.datetimeFormatterParams() }, { title: Vue.computed(() => this.preloadedPhrasen.gueltigBisPhrase), field: "Gültig_bis", - headerFilter: true, + headerFilterFunc: 'dates', + headerFilter: dateFilter, resizable: true, minWidth: 200, - visible: true + visible: true, + formatter:"datetime", + formatterParams: this.datetimeFormatterParams() }, { title: Vue.computed(() => this.preloadedPhrasen.wochenstundenPhrase), @@ -102,7 +109,7 @@ export default { persistence: { columns: false }, - height: 300, + minHeight: 300, layout: "fitColumns", responsiveLayout: "collapse", responsiveLayoutCollapseUseFormatters: false, @@ -138,9 +145,12 @@ export default { { title: Vue.computed(() => this.preloadedPhrasen.ausgabedatumPhrase), field: "Ausgegeben_am", - headerFilter: true, + headerFilterFunc: 'dates', + headerFilter: dateFilter, minWidth: 200, - visible: true + visible: true, + formatter:"datetime", + formatterParams: this.datetimeFormatterParams() }, ], } @@ -211,6 +221,15 @@ export default { setTableColumnTitles() { // reevaluates computed phrasen if(this.$refs.betriebsmittelTable) this.$refs.betriebsmittelTable.tabulator.setColumns(this.betriebsmittel_table_options.columns) if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns) + }, + datetimeFormatterParams: function() { + const params = { + inputFormat:"yyyy-MM-dd", + outputFormat:"dd.MM.yyyy", + invalidPlaceholder:"(invalid date)", + timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone + }; + return params; } }, diff --git a/public/js/components/Cis/Profil/MitarbeiterViewProfil.js b/public/js/components/Cis/Profil/MitarbeiterViewProfil.js index c1ce4ead6..481714590 100644 --- a/public/js/components/Cis/Profil/MitarbeiterViewProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterViewProfil.js @@ -5,6 +5,8 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js"; import ProfilEmails from "./ProfilComponents/ProfilEmails.js"; import ProfilInformation from "./ProfilComponents/ProfilInformation.js"; +import { dateFilter } from '../../../tabulator/filters/Dates.js'; + export default { components: { CoreFilterCmpt, @@ -24,7 +26,7 @@ export default { persistence: { columns: false }, - height: 300, + minHeight: 300, layout: "fitColumns", responsiveLayout: "collapse", responsiveLayoutCollapseUseFormatters: false, @@ -60,18 +62,24 @@ export default { { title: Vue.computed(() => this.$p.t('global/gueltigVon')), field: "Gültig_von", - headerFilter: true, + headerFilterFunc: 'dates', + headerFilter: dateFilter, resizable: true, minWidth: 200, - visible: true + visible: true, + formatter:"datetime", + formatterParams: this.datetimeFormatterParams() }, { title: Vue.computed(() => this.$p.t('global/gueltigBis')), field: "Gültig_bis", - headerFilter: true, + headerFilterFunc: 'dates', + headerFilter: dateFilter, resizable: true, minWidth: 200, - visible: true + visible: true, + formatter:"datetime", + formatterParams: this.datetimeFormatterParams() }, { title: Vue.computed(() => this.$p.t('profil/wochenstunden')), @@ -91,6 +99,15 @@ export default { funktionenTableBuilt: function () { this.$refs.funktionenTable.tabulator.setData(this.data.funktionen); }, + datetimeFormatterParams: function() { + const params = { + inputFormat:"yyyy-MM-dd", + outputFormat:"dd.MM.yyyy", + invalidPlaceholder:"(invalid date)", + timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone + }; + return params; + } }, watch: { 'data.funktionen'(newVal) { diff --git a/public/js/components/Cis/Profil/ProfilComponents/RoleInformation.js b/public/js/components/Cis/Profil/ProfilComponents/RoleInformation.js index ae1a4f2b9..968fb2f83 100644 --- a/public/js/components/Cis/Profil/ProfilComponents/RoleInformation.js +++ b/public/js/components/Cis/Profil/ProfilComponents/RoleInformation.js @@ -10,21 +10,34 @@ export default { type: String, } }, - inject: [ - 'studiengang_kz', // inject info that should not be displayed - ], + inject: { + // inject info that should not be displayed + 'studiengang_kz': { + from: 'studiengang_kz', + default: false + }, + }, computed: { getLinkGruppeListe() { + if(this.studiengang_kz === false) { + return ''; + } return this.data.gruppe?.value && this.data.verband?.value && this.data.semester?.value ? FHC_JS_DATA_STORAGE_OBJECT.app_root + 'cis/private/stud_in_grp.php?kz='+this.studiengang_kz+'&sem=' + this.data.semester.value + '&verband=' + this.data.verband.value + '&grp=' + this.data.gruppe.value : '' }, getLinkVerbandListe() { + if(this.studiengang_kz === false) { + return ''; + } return this.data.verband?.value && this.data.semester?.value ? FHC_JS_DATA_STORAGE_OBJECT.app_root + 'cis/private/stud_in_grp.php?kz='+this.studiengang_kz+'&sem=' + this.data.semester.value + '&verband=' + this.data.verband.value : '' }, getLinkSemesterListe() { + if(this.studiengang_kz === false) { + return ''; + } return this.data.semester?.value ? FHC_JS_DATA_STORAGE_OBJECT.app_root + 'cis/private/stud_in_grp.php?kz='+this.studiengang_kz+'&sem=' + this.data.semester.value : '' } diff --git a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/ImageUpload.js b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/ImageUpload.js index 672b4ce4a..2d2efb0fe 100644 --- a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/ImageUpload.js +++ b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/ImageUpload.js @@ -16,7 +16,7 @@ export default { mixins: [BsModal], props: { titel: { - type: Object, + type: String, }, files: { type: Array, @@ -57,7 +57,7 @@ export default { }, template: /*html*/` - + diff --git a/public/js/components/Cis/Profil/StudentProfil.js b/public/js/components/Cis/Profil/StudentProfil.js index 8d46377d5..3f8e8380d 100644 --- a/public/js/components/Cis/Profil/StudentProfil.js +++ b/public/js/components/Cis/Profil/StudentProfil.js @@ -11,6 +11,7 @@ import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js"; import EditProfil from "./ProfilModal/EditProfil.js"; import ApiProfilUpdate from '../../../api/factory/profilUpdate.js'; +import { dateFilter } from '../../../tabulator/filters/Dates.js'; export default { components: { @@ -39,7 +40,7 @@ export default { persistence: { columns: false }, - height: 200, + minHeight: 200, layout: "fitColumns", columns: [{ title: Vue.computed(() => this.preloadedPhrasen.zutrittsGruppenPhrase), @@ -51,7 +52,7 @@ export default { persistence: { columns: false }, - height: 300, + minHeight: 300, layout: "fitColumns", responsiveLayout: "collapse", responsiveLayoutCollapseUseFormatters: false, @@ -85,9 +86,12 @@ export default { { title: Vue.computed(() =>this.preloadedPhrasen.ausgabedatum) , field: "Ausgegeben_am", - headerFilter: true, + headerFilterFunc: 'dates', + headerFilter: dateFilter, minWidth: 200, - visible: true + visible: true, + formatter:"datetime", + formatterParams: this.datetimeFormatterParams() }, ], }, @@ -160,6 +164,15 @@ export default { this.$refs.editModal.show(); }); }, + datetimeFormatterParams: function() { + const params = { + inputFormat:"yyyy-MM-dd", + outputFormat:"dd.MM.yyyy", + invalidPlaceholder:"(invalid date)", + timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone + }; + return params; + } }, computed: { From d062e8903f3453c0730a9e000f6813f592278c12 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Fri, 19 Sep 2025 11:29:08 +0200 Subject: [PATCH 182/312] Mitarbeiter model searchMitarbeiter method: removed redundant person id --- application/models/ressource/Mitarbeiter_model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/models/ressource/Mitarbeiter_model.php b/application/models/ressource/Mitarbeiter_model.php index 0dad7443e..a50112761 100644 --- a/application/models/ressource/Mitarbeiter_model.php +++ b/application/models/ressource/Mitarbeiter_model.php @@ -358,7 +358,7 @@ class Mitarbeiter_model extends DB_Model if ($mode == "mitAkadGrad") $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"; From 3f5801f6ed92f8861d8f39ff89a213fdbccb26b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96sterreicher?= Date: Mon, 22 Sep 2025 09:26:13 +0200 Subject: [PATCH 183/312] =?UTF-8?q?Men=C3=BCpunkt=20f=C3=BCr=20LV-Verwaltu?= =?UTF-8?q?ng=20im=20Vilesci=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/config/navigation.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/application/config/navigation.php b/application/config/navigation.php index 5cbb6512c..11fd1a3a6 100644 --- a/application/config/navigation.php +++ b/application/config/navigation.php @@ -64,7 +64,7 @@ $config['navigation_header'] = array( 'lehrveranstaltungen' => array( 'link' => site_url('lehre/lvplanung/LvTemplateUebersicht'), 'icon' => '', - 'description' => 'Lehrveranstaltungen', + 'description' => 'Lehrveranstaltungen Templates', 'sort' => 15 ), 'reihungstest' => array( @@ -81,6 +81,16 @@ $config['navigation_header'] = array( 'sort' => 30, 'requiredPermissions' => 'infocenter:r' ), + 'lvverwaltung' => array( + 'link' => site_url('LVVerwaltung'), + 'icon' => '', + 'description' => 'LV Verwaltung', + 'requiredPermissions' => array( + 'admin:r', + 'assistenz:r' + ), + 'sort' => 35 + ), 'lehrauftrag' => array( 'link' => site_url('lehre/lehrauftrag/Lehrauftrag/Dashboard'), 'description' => 'Lehrauftrag', From ef56af633790177d4010a78e82e5abed9bcb61ff Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 22 Sep 2025 09:40:37 +0200 Subject: [PATCH 184/312] lv teil hinzufuegen beim semesterwechsel das richtige semester preselecten lv teil kopieren nur bei lv teilen anzeigen --- public/js/components/LVVerwaltung/LVVerwaltung.js | 12 +++++------- public/js/components/LVVerwaltung/Lektor/Daten.js | 4 ++-- public/js/components/LVVerwaltung/Lektor/Vertrag.js | 1 - public/js/components/LVVerwaltung/Setup/Table.js | 12 ++++++++++-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index a1ff28a2e..c03e7c84c 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -32,13 +32,7 @@ export default { studiensemester_kurzbz: { type: String, required: false, default: null }, emp: { type: String, required: false, default: null } }, - computed: { - selectedStudiensemester() { - return this.studiensemester_kurzbz != null - ? this.studiensemester_kurzbz - : this.defaultSemester; - } - }, + provide() { return { currentSemester: Vue.computed(() => this.selectedStudiensemester), @@ -70,12 +64,16 @@ export default { emp() { this.updateFilter(); }, + studiensemester_kurzbz(newVal) { + this.selectedStudiensemester = newVal ?? this.defaultSemester; + } }, data() { return { selected: [], studiengang: "", filter: {}, + selectedStudiensemester: this.studiensemester_kurzbz ?? this.defaultSemester, endpoint: ApiStudiengangTree, dropdowns: { studiensemester_array: [], diff --git a/public/js/components/LVVerwaltung/Lektor/Daten.js b/public/js/components/LVVerwaltung/Lektor/Daten.js index d4c6aa5e3..a9c7e53be 100644 --- a/public/js/components/LVVerwaltung/Lektor/Daten.js +++ b/public/js/components/LVVerwaltung/Lektor/Daten.js @@ -198,7 +198,7 @@ export default{ @@ -230,7 +230,7 @@ export default{ -
    +
    diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index 2f37e4207..309960f54 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -56,7 +56,13 @@ export default { } }, deep: true, - + }, + currentSemester: { + handler(newVal) + { + this.lv_info_default.studiensemester_kurzbz = newVal + this.lv_info = false; + } } }, data() { @@ -137,6 +143,8 @@ export default { selectableRows: true, rowContextMenu: (component, e) => { + if (e.getData()?.lehreinheit_id === undefined) + return; return [ { label: "LV-Teil kopieren", @@ -159,7 +167,7 @@ export default { label: "Nur mit Gruppen", action: (e, row) => { - this.copyLehreinheit(row, "halb"); + this.copyLehreinheit(row, "gruppen"); }, }, { From c69877ed85eaf7efc840348b889377e6ec9de91f Mon Sep 17 00:00:00 2001 From: ma0048 Date: Mon, 22 Sep 2025 13:41:58 +0200 Subject: [PATCH 185/312] bug beim loeschen von einer notiz mit doc --- application/core/Notiz_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/core/Notiz_Controller.php b/application/core/Notiz_Controller.php index c2bb03267..cfc54d5f5 100644 --- a/application/core/Notiz_Controller.php +++ b/application/core/Notiz_Controller.php @@ -393,10 +393,10 @@ abstract class Notiz_Controller extends FHCAPI_Controller foreach ($result as $doc) { $res = $this->dmslib->removeAll($doc->dms_id); - if (isError($result)) + if (isError($res)) { $this->db->trans_rollback(); - $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + $this->terminateWithError(getError($res), self::ERROR_TYPE_GENERAL); } } From a533294121fe0ba95ca5b0253d7fd26c86b60c38 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 22 Sep 2025 15:54:26 +0200 Subject: [PATCH 186/312] removed addMeta in Student.php Studierendenverwaltung --- application/controllers/api/frontend/v1/stv/Student.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Student.php b/application/controllers/api/frontend/v1/stv/Student.php index 122bbb4c8..d1f259cc4 100644 --- a/application/controllers/api/frontend/v1/stv/Student.php +++ b/application/controllers/api/frontend/v1/stv/Student.php @@ -460,8 +460,6 @@ class Student extends FHCAPI_Controller $_POST['ausbildungssemester'] = 0; } - $this->addMeta('test', $_POST['personOnly']); - $this->load->library('form_validation'); $this->form_validation->set_rules('nachname', 'Nachname', 'callback_requiredIfNotPersonId', [ From 3f0f48a4e088760766561b308eed047ae00bc68b Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 22 Sep 2025 17:34:50 +0200 Subject: [PATCH 187/312] Student.php: bracket bugfix, return person id of added Personinstead of true, Studierendenverwaltung Projektarbeit phrases --- application/controllers/api/frontend/v1/stv/Student.php | 8 +++++--- .../Details/Projektarbeit/Projektbetreuer.js | 2 +- system/phrasesupdate.php | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Student.php b/application/controllers/api/frontend/v1/stv/Student.php index d1f259cc4..23a8fc7cb 100644 --- a/application/controllers/api/frontend/v1/stv/Student.php +++ b/application/controllers/api/frontend/v1/stv/Student.php @@ -513,7 +513,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() @@ -695,7 +697,7 @@ class Student extends FHCAPI_Controller //Studentendatensatz anlegen //StudentLehrverband anlegen } - + } // TODO(chris): DEBUG /*$result = $this->PrestudentModel->loadWhere([ 'pestudent_id' => 1 @@ -704,7 +706,7 @@ class Student extends FHCAPI_Controller return $result; }*/ - return success(true); + return success($person_id); } public function requiredIfNotPersonId($value) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index 8416dadd4..adcf2f2a8 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -391,7 +391,7 @@ export default { // stuff to do after new person has been saved personSaved(result) { this.$api - .call(ApiStvProjektbetreuer.getPerson(result.person_id)) + .call(ApiStvProjektbetreuer.getPerson(result)) .then(response => { // set the new person in Betreuer autocomplete field this.autocompleteSelectedBetreuer = response.data; diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 41cf4c9cf..97b556239 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -49801,7 +49801,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Projektbetreuer ungültig', + 'text' => 'ProjektbetreuerIn ungültig', 'description' => '', 'insertvon' => 'system' ), @@ -49821,7 +49821,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Löschen nicht möglich, Projektbetreuer hat bereits einen Vertrag', + 'text' => 'Löschen nicht möglich, ProjektbetreuerIn hat bereits einen Vertrag', 'description' => '', 'insertvon' => 'system' ), @@ -50261,7 +50261,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Betreuer bearbeiten', + 'text' => 'BetreuerIn bearbeiten', 'description' => '', 'insertvon' => 'system' ), @@ -50281,7 +50281,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Betreuer speichern', + 'text' => 'BetreuerIn speichern', 'description' => '', 'insertvon' => 'system' ), From 5efad50b62e4c27d18dd608f66f0c0a17efbee8f Mon Sep 17 00:00:00 2001 From: ma0048 Date: Tue, 23 Sep 2025 08:31:00 +0200 Subject: [PATCH 188/312] added missing iframe component --- .../Cis/Cms/Content_types/Iframe_content.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 public/js/components/Cis/Cms/Content_types/Iframe_content.js diff --git a/public/js/components/Cis/Cms/Content_types/Iframe_content.js b/public/js/components/Cis/Cms/Content_types/Iframe_content.js new file mode 100644 index 000000000..568cfaf03 --- /dev/null +++ b/public/js/components/Cis/Cms/Content_types/Iframe_content.js @@ -0,0 +1,31 @@ +import { replaceRelativeLegacyLink } from "../../../../helpers/LegacyLinkReplaceHelper.js"; + +export default { + name: "iframe_content", + props: { + content: { type: String, required: true } + }, + computed: { + srcUrl() { + const parser = new DOMParser() + const doc = parser.parseFromString(`
    ${this.content}
    `, "text/html"); + const iframe = doc.querySelector("iframe[src]"); + + if (!iframe) + return ""; + + let url = iframe.getAttribute("src") || ""; + return replaceRelativeLegacyLink(url); + } + }, + template: ` +
    + +
    Keine URL gefunden.
    +
    + ` +}; From 6abdb25c0a40d83498c690c4133cf9a305b3fc39 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 24 Sep 2025 09:33:41 +0200 Subject: [PATCH 189/312] abort controller fixed und index auf campus.tbl_studierendenantrag_status.studierendenantrag_id hinzugefuegt --- public/js/api/factory/studstatus/leitung.js | 2 +- .../Studierendenantrag/Leitung/Actions/New.js | 28 +++++++------------ system/dbupdate_3.4.php | 1 + ...udstatus_suche_abort_controller_haengt.php | 16 +++++++++++ 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 system/dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php diff --git a/public/js/api/factory/studstatus/leitung.js b/public/js/api/factory/studstatus/leitung.js index cc6b87d25..7f6bc9f54 100644 --- a/public/js/api/factory/studstatus/leitung.js +++ b/public/js/api/factory/studstatus/leitung.js @@ -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', diff --git a/public/js/components/Studierendenantrag/Leitung/Actions/New.js b/public/js/components/Studierendenantrag/Leitung/Actions/New.js index a6cd69219..f78455baa 100644 --- a/public/js/components/Studierendenantrag/Leitung/Actions/New.js +++ b/public/js/components/Studierendenantrag/Leitung/Actions/New.js @@ -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: ` diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 4b52b04c4..7b298df36 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -79,6 +79,7 @@ require_once('dbupdate_3.4/61730_Dashboard_Anpassungen.php'); require_once('dbupdate_3.4/40128_search.php'); require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php'); require_once('dbupdate_3.4/66982_berufsschule.php'); +require_once('dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

    Pruefe Tabellen und Attribute!

    '; diff --git a/system/dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php b/system/dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php new file mode 100644 index 000000000..4c348a7ab --- /dev/null +++ b/system/dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php @@ -0,0 +1,16 @@ +db_query("SELECT * FROM pg_class WHERE relname='idx_tbl_studierendenantrag_status_studierendenantrag_id'")) +{ + if ($db->db_num_rows($result) == 0) + { + $qry = "CREATE INDEX idx_tbl_studierendenantrag_status_studierendenantrag_id ON campus.tbl_studierendenantrag_status USING btree (studierendenantrag_id)"; + + if (!$db->db_query($qry)) + echo 'Indizes: ' . $db->db_last_error() . '
    '; + else + echo 'Index fuer campus.tbl_studierendenantrag_status.studierendenantrag_id hinzugefuegt'; + } +} From 0f43e8eb7aea87235c8b22dfc0d191d19f0b4691 Mon Sep 17 00:00:00 2001 From: ma0048 Date: Wed, 24 Sep 2025 14:27:01 +0200 Subject: [PATCH 190/312] autocomplete auf backend umgestellt textarea formattedAnmerkung rausgenommen ui angepasst --- .../controllers/api/frontend/v1/lv/Gruppe.php | 70 +++++- .../controllers/api/frontend/v1/lv/Lektor.php | 26 ++- .../controllers/api/frontend/v1/lv/Tags.php | 2 +- application/libraries/LektorLib.php | 12 +- .../education/Lehreinheitgruppe_model.php | 4 +- public/js/api/lehrveranstaltung/gruppe.js | 10 +- public/js/api/lehrveranstaltung/lektor.js | 5 +- .../LVVerwaltung/Details/Direktinskription.js | 44 ++-- .../components/LVVerwaltung/Details/Form.js | 12 +- .../LVVerwaltung/Details/Gruppen.js | 50 +++-- .../components/LVVerwaltung/LVVerwaltung.js | 49 +++-- .../components/LVVerwaltung/Lektor/Daten.js | 89 +++++--- .../components/LVVerwaltung/Lektor/Table.js | 44 ++-- .../components/LVVerwaltung/Lektor/Vertrag.js | 57 ++--- .../js/components/LVVerwaltung/Setup/Table.js | 4 +- .../Stv/Studentenverwaltung/Verband.js | 3 + system/phrasesupdate.php | 200 ++++++++++++++++++ 17 files changed, 509 insertions(+), 172 deletions(-) diff --git a/application/controllers/api/frontend/v1/lv/Gruppe.php b/application/controllers/api/frontend/v1/lv/Gruppe.php index 83a4fb696..a573c3ea3 100644 --- a/application/controllers/api/frontend/v1/lv/Gruppe.php +++ b/application/controllers/api/frontend/v1/lv/Gruppe.php @@ -12,8 +12,8 @@ class Gruppe extends FHCAPI_Controller 'add' => ['admin:rw', 'assistenz:rw'], 'delete' => ['admin:rw', 'assistenz:rw'], 'deleteFromLVPlan' => ['admin:rw', 'assistenz:rw'], - 'getBenutzer' => ['admin:r', 'assistenz:r'], - 'getAll' => ['admin:r', 'assistenz:r'], + 'getBenutzerSearch' => ['admin:r', 'assistenz:r'], + 'getAllSearch' => ['admin:r', 'assistenz:r'], 'getByLehreinheit' => ['admin:r', 'assistenz:r'], ]); @@ -22,7 +22,8 @@ class Gruppe extends FHCAPI_Controller $this->_ci->load->library('PhrasesLib'); $this->loadPhrases( array( - 'ui' + 'ui', + 'lehre' ) ); @@ -91,11 +92,11 @@ class Gruppe extends FHCAPI_Controller $lehreinheitgruppe_id = $this->input->post('lehreinheitgruppe_id'); if (is_null($lehreinheit_id) || !ctype_digit((string)$lehreinheit_id) || is_null($lehreinheitgruppe_id) || !ctype_digit((string)$lehreinheitgruppe_id)) - $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); $lehreinheitgruppe_result = $this->_ci->LehreinheitgruppeModel->loadWhere(array('lehreinheitgruppe_id' => $lehreinheitgruppe_id)); if (!hasData($lehreinheitgruppe_result) || isError($lehreinheitgruppe_result)) - $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); $this->checkPermission($lehreinheit_id); @@ -108,15 +109,30 @@ class Gruppe extends FHCAPI_Controller } - public function getAll() + public function getAllSearch($query = null) { + if (is_null($query)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + $query_words = explode(' ', urldecode($query)); + $this->_ci->GruppeModel->addSelect('gruppe_kurzbz, studiengang_kz, semester, bezeichnung, gid, \'false\' as lehrverband'); - $gruppen_result = $this->_ci->GruppeModel->loadWhere(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false, 'semester IS NOT NULL' => null)); + $this->_ci->GruppeModel->db->where(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false, 'semester IS NOT NULL' => null)); + $this->_ci->GruppeModel->db->group_start(); + foreach ($query_words as $word) + { + $this->_ci->GruppeModel->db->group_start(); + $this->_ci->GruppeModel->db->where('gruppe_kurzbz ILIKE', "%" . $word . "%"); + $this->_ci->GruppeModel->db->or_where('bezeichnung ILIKE', "%" . $word . "%"); + $this->_ci->GruppeModel->db->group_end(); + } + $this->_ci->GruppeModel->db->group_end(); + + $gruppen_result = $this->_ci->GruppeModel->load(); $gruppen_array = array(); @@ -135,7 +151,18 @@ class Gruppe extends FHCAPI_Controller $this->_ci->LehrverbandModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); $this->_ci->LehrverbandModel->addOrder('verband'); $this->_ci->LehrverbandModel->addOrder('gruppe'); - $lehrverband_result = $this->_ci->LehrverbandModel->loadWhere(array('tbl_lehrverband.aktiv' => true)); + $this->_ci->LehrverbandModel->db->where(array('tbl_lehrverband.aktiv' => true)); + + $this->_ci->LehrverbandModel->db->group_start(); + foreach ($query_words as $word) + { + $this->_ci->LehrverbandModel->db->group_start(); + $this->_ci->LehrverbandModel->db->where('CONCAT(CONCAT(typ, kurzbz), \'\', semester, verband, COALESCE(gruppe,\'\')) ILIKE', "%" . $word . "%"); + $this->_ci->LehrverbandModel->db->or_where('tbl_lehrverband.bezeichnung ILIKE', "%" . $word . "%"); + $this->_ci->LehrverbandModel->db->group_end(); + } + $this->_ci->LehrverbandModel->db->group_end(); + $lehrverband_result = $this->_ci->LehrverbandModel->load(); $lehrverband_array = array(); @@ -150,15 +177,38 @@ class Gruppe extends FHCAPI_Controller $this->terminateWithSuccess($all_gruppen); } - public function getBenutzer() + public function getBenutzerSearch($query = null) { + if (is_null($query)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $query_words = explode(' ', urldecode($query)); + $this->_ci->PersonModel->addSelect('vorname, nachname, uid, semester, UPPER(CONCAT(tbl_studiengang.typ, tbl_studiengang.kurzbz)) as studiengang'); $this->_ci->PersonModel->addJoin('public.tbl_benutzer', 'person_id'); $this->_ci->PersonModel->addJoin('public.tbl_mitarbeiter', 'uid = mitarbeiter_uid', 'LEFT'); $this->_ci->PersonModel->addJoin('public.tbl_student', 'uid = student_uid', 'LEFT'); $this->_ci->PersonModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT'); - $personen = $this->_ci->PersonModel->loadWhere(array('tbl_benutzer.aktiv' => true)); + $this->_ci->PersonModel->db->where(array('tbl_benutzer.aktiv' => true)); + + $this->_ci->PersonModel->db->group_start(); + foreach ($query_words as $word) + { + $this->_ci->PersonModel->db->group_start(); + $this->_ci->PersonModel->db->where('tbl_person.vorname ILIKE', "%" . $word . "%"); + $this->_ci->PersonModel->db->or_where('tbl_person.nachname ILIKE', "%" . $word . "%"); + $this->_ci->PersonModel->db->or_where('uid ILIKE', "%" . $word . "%"); + $this->_ci->PersonModel->db->or_where('CONCAT(tbl_studiengang.typ, tbl_studiengang.kurzbz) ILIKE', "%" . $word . "%"); + + if (is_numeric($word)) + { + $this->_ci->PersonModel->db->or_where('semester', $word); + } + $this->_ci->PersonModel->db->group_end(); + } + $this->_ci->PersonModel->db->group_end(); + $personen = $this->_ci->PersonModel->load(); $this->terminateWithSuccess(hasData($personen) ? getData($personen) : array()); } diff --git a/application/controllers/api/frontend/v1/lv/Lektor.php b/application/controllers/api/frontend/v1/lv/Lektor.php index 68bec1bc4..ccdc79133 100644 --- a/application/controllers/api/frontend/v1/lv/Lektor.php +++ b/application/controllers/api/frontend/v1/lv/Lektor.php @@ -15,7 +15,7 @@ class Lektor extends FHCAPI_Controller 'deleteLVPlan' => ['admin:rw', 'assistenz:rw'], 'deletePerson' => ['admin:rw', 'assistenz:rw'], 'getLehrfunktionen' => ['admin:r', 'assistenz:r'], - 'getLektoren' => ['admin:r', 'assistenz:r'], + 'getLektorenSearch' => ['admin:r', 'assistenz:r'], 'getLektorenByLE' => ['admin:r', 'assistenz:r'], 'getLektorDaten' => ['admin:r', 'assistenz:r'], 'getLektorVertrag' => ['admin:r', 'assistenz:r'], @@ -208,12 +208,32 @@ class Lektor extends FHCAPI_Controller $this->terminateWithSuccess(getData($this->_ci->LehrfunktionModel->load())); } - public function getLektoren() + public function getLektorenSearch($query = null) { + if (is_null($query)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + $query_words = explode(' ', urldecode($query)); + $this->_ci->MitarbeiterModel->addSelect('uid, person_id, vorname, nachname'); $this->_ci->MitarbeiterModel->addJoin('public.tbl_benutzer', 'uid = mitarbeiter_uid'); $this->_ci->MitarbeiterModel->addJoin('public.tbl_person', 'person_id'); - $this->terminateWithSuccess(getData($this->_ci->MitarbeiterModel->loadWhere(array('public.tbl_benutzer.aktiv' => true)))); + + $this->_ci->MitarbeiterModel->db->where('public.tbl_benutzer.aktiv', true); + + $this->_ci->MitarbeiterModel->db->group_start(); + foreach ($query_words as $word) + { + $this->_ci->MitarbeiterModel->db->group_start(); + $this->_ci->MitarbeiterModel->db->where('tbl_person.vorname ILIKE', "%" . $word . "%"); + $this->_ci->MitarbeiterModel->db->or_where('tbl_person.nachname ILIKE', "%" . $word . "%"); + $this->_ci->MitarbeiterModel->db->or_where('uid ILIKE', "%" . $word . "%"); + $this->_ci->MitarbeiterModel->db->group_end(); + } + $this->_ci->MitarbeiterModel->db->group_end(); + $this->_ci->MitarbeiterModel->addOrder('nachname'); + $this->_ci->MitarbeiterModel->addOrder('vorname'); + $result = $this->_ci->MitarbeiterModel->load(); + $this->terminateWithSuccess(hasData($result) ? getData($result) : array()); } private function checkLehreinheit($lehreinheit_id) diff --git a/application/controllers/api/frontend/v1/lv/Tags.php b/application/controllers/api/frontend/v1/lv/Tags.php index a22a4c82e..34c42bc32 100644 --- a/application/controllers/api/frontend/v1/lv/Tags.php +++ b/application/controllers/api/frontend/v1/lv/Tags.php @@ -5,7 +5,7 @@ if (!defined('BASEPATH')) class Tags extends Tag_Controller { - const BERECHTIGUNG_KURZBZ = ['admin:rw', 'assistenz:rw']; + const BERECHTIGUNG_KURZBZ = ['admin:rw', 'assistenz:r']; public function __construct() { diff --git a/application/libraries/LektorLib.php b/application/libraries/LektorLib.php index e06e2b728..bbe630eaf 100644 --- a/application/libraries/LektorLib.php +++ b/application/libraries/LektorLib.php @@ -17,6 +17,7 @@ class LektorLib $this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); $this->_ci->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel'); $this->_ci->load->model('person/Benutzer_model', 'BenutzerModel'); + $this->_ci->load->library('PhrasesLib', array('lehre')); } public function addLektorToLehreinheit($lehreinheit_id, $mitarbeiter_uid) @@ -35,7 +36,7 @@ class LektorLib if (isError($already_assigned)) return $already_assigned; - if (hasData($already_assigned)) return error('Lektor already assigned'); + if (hasData($already_assigned)) return error($this->_ci->phraseslib->t("lehre", "bereitzugeteilt")); $studiensemester_result = $this->_ci->StudiensemesterModel->loadWhere(array('studiensemester_kurzbz' => $lehreinheit->studiensemester_kurzbz)); if (isError($studiensemester_result)) return $studiensemester_result; @@ -88,6 +89,7 @@ class LektorLib $lehreinheit = getData($lehreinheit_result)[0]; + //TODO kollision check, wird vorerst nicht implementiert -> nur über das FAS möglich if (isset($new_data['mitarbeiter_uid']) && $new_data['mitarbeiter_uid'] !== $mitarbeiter_uid) { @@ -98,7 +100,13 @@ class LektorLib $verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); if (hasData($verplant)) - return error('Wechsel vom Mitarbeiter nicht möglich da er bereits verplant ist!'); + return error($this->_ci->phraseslib->t("lehre", "lektorbereitsverplant")); + + $lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_data['mitarbeiter_uid'], 'lehreinheit_id' => $lehreinheit_id)); + + if (hasData($lehreinheit_data)) + return error($this->_ci->phraseslib->t("lehre", "bereitzugeteilt")); + } $warning = ''; if (isset($new_data['semesterstunden'])) diff --git a/application/models/education/Lehreinheitgruppe_model.php b/application/models/education/Lehreinheitgruppe_model.php index bca395f31..dee8bbfe1 100644 --- a/application/models/education/Lehreinheitgruppe_model.php +++ b/application/models/education/Lehreinheitgruppe_model.php @@ -374,7 +374,7 @@ class Lehreinheitgruppe_model extends DB_Model return success('Group assigned successfully to Lehreinheit'); } else - return error('Group already assigned'); + return error($this->p->t('lehre', 'grpbereitszugeteilt')); } public function deleteGroup($lehreinheit_id, $lehreinheitgruppe_id) @@ -401,7 +401,7 @@ class Lehreinheitgruppe_model extends DB_Model $stundenplan_result = $this->loadWhere(array('tbl_lehreinheitgruppe.lehreinheitgruppe_id' => $lehreinheitgruppe_id)); if (hasData($stundenplan_result)) - return error('Gruppe already verplant'); + return error($this->p->t('lehre', 'grpbereitsverplant')); $delete_result = $this->delete($lehreinheitgruppe_id); diff --git a/public/js/api/lehrveranstaltung/gruppe.js b/public/js/api/lehrveranstaltung/gruppe.js index e22fdd847..386938160 100644 --- a/public/js/api/lehrveranstaltung/gruppe.js +++ b/public/js/api/lehrveranstaltung/gruppe.js @@ -37,20 +37,20 @@ export default { /*------------- details -------- */ - getAll() + getBenutzerSearch(query) { return { method: 'get', - url: '/api/frontend/v1/lv/gruppe/getAll/' + url: '/api/frontend/v1/lv/gruppe/getBenutzerSearch/' + encodeURIComponent(query) }; }, - getBenutzer() + getAllSearch(query) { return { method: 'get', - url: '/api/frontend/v1/lv/gruppe/getBenutzer/' + url: '/api/frontend/v1/lv/gruppe/getAllSearch/' + encodeURIComponent(query) }; - } + }, } diff --git a/public/js/api/lehrveranstaltung/lektor.js b/public/js/api/lehrveranstaltung/lektor.js index e8c2fb673..9539315aa 100644 --- a/public/js/api/lehrveranstaltung/lektor.js +++ b/public/js/api/lehrveranstaltung/lektor.js @@ -8,12 +8,11 @@ export default { }; }, - - getLektoren() + getLektorenSearch(query) { return { method: 'get', - url: '/api/frontend/v1/lv/lektor/getLektoren/' + url: '/api/frontend/v1/lv/lektor/getLektorenSearch/' + encodeURIComponent(query) }; }, diff --git a/public/js/components/LVVerwaltung/Details/Direktinskription.js b/public/js/components/LVVerwaltung/Details/Direktinskription.js index c7b8e5894..268b1bf28 100644 --- a/public/js/components/LVVerwaltung/Details/Direktinskription.js +++ b/public/js/components/LVVerwaltung/Details/Direktinskription.js @@ -2,6 +2,7 @@ import {CoreFilterCmpt} from "../../filter/Filter.js"; import FormForm from '../../Form/Form.js'; import FormInput from '../../Form/Input.js'; import ApiDirektGruppe from "../../../api/lehrveranstaltung/direktgruppe.js"; +import ApiGruppe from "../../../api/lehrveranstaltung/gruppe.js"; export default{ name: "LVDirektGruppen", components: { @@ -65,7 +66,6 @@ export default{ selectedUser: null, filteredUsers: [], abortController: null, - searchTimeout: null, } }, watch: { @@ -97,18 +97,36 @@ export default{ }, searchUser(event) { - const query = event.query.toLowerCase().trim(); - this.filteredUsers = this.dropdowns.benutzer_array.filter(user => { + const query = event.query.trim(); + if (!query) + { + this.filteredUsers = []; + return; + } - const fullName = `${user.vorname.toLowerCase()} ${user.nachname.toLowerCase()}`; - const reverseFullName = `${user.nachname.toLowerCase()} ${user.vorname.toLowerCase()}`; - return fullName.includes(query) || reverseFullName.includes(query) || user.uid.toLowerCase().includes(query) || user.studiengang.toLowerCase().includes(query); - }).map(user => ({ - label: user.studiengang - ? `${user.nachname} ${user.vorname} ${user.uid} ${user.studiengang} ${user.semester}` - : `${user.nachname} ${user.vorname} ${user.uid}`, - uid: user.uid - })); + if (query.length < 2) + { + return; + } + + if (this.abortController) + { + this.abortController.abort(); + } + + this.abortController = new AbortController(); + const signal = this.abortController.signal; + + this.$api.call(ApiGruppe.getBenutzerSearch(query), { signal }) + .then(result => { + this.filteredUsers = result.data.map(user => ({ + label: user.studiengang + ? `${user.nachname} ${user.vorname} ${user.uid} ${user.studiengang} ${user.semester}` + : `${user.nachname} ${user.vorname} ${user.uid}`, + uid: user.uid + }) + )}) + .catch(this.$fhcAlert.handleSystemError) }, addUser() { @@ -144,7 +162,7 @@ export default{ :placeholder="$p.t('lehre', 'assignPerson')" v-model="selectedUser" field="label" - :minLength="3" + :minLength="2" @item-select="addUser" @complete="searchUser" > diff --git a/public/js/components/LVVerwaltung/Details/Form.js b/public/js/components/LVVerwaltung/Details/Form.js index 5908512d7..d8299e4fa 100644 --- a/public/js/components/LVVerwaltung/Details/Form.js +++ b/public/js/components/LVVerwaltung/Details/Form.js @@ -26,16 +26,6 @@ export default { default: true } }, - computed: { - formattedAnmerkung: { - get() { - return (this.data.anmerkung || '').replace(/\\n/g, '\n'); - }, - set(value) { - this.data.anmerkung = (value || '').replace(/\n/g, '\\n'); - } - } - }, template: `
    @@ -74,7 +64,7 @@ export default { :label="$p.t('lehre', 'detailanmerkung')" type="textarea" container-class="col-3" - v-model="formattedAnmerkung" + v-model="data.anmerkung" name="anmerkung" id="anmerkung" rows="10" diff --git a/public/js/components/LVVerwaltung/Details/Gruppen.js b/public/js/components/LVVerwaltung/Details/Gruppen.js index a6f9df005..57dc49728 100644 --- a/public/js/components/LVVerwaltung/Details/Gruppen.js +++ b/public/js/components/LVVerwaltung/Details/Gruppen.js @@ -90,7 +90,8 @@ export default{ tabulatorEvents: [], showAutocomplete: false, filteredGroups: [], - selectedGroup: null + selectedGroup: null, + abortController: null } }, watch: { @@ -99,19 +100,42 @@ export default{ } }, methods:{ - searchGroup(event) + + async searchGroup(event) { - const query = event.query.toLowerCase().trim(); - this.filteredGroups = this.dropdowns.gruppen_array.filter(gruppe => { - return gruppe.gruppe_kurzbz.toLowerCase().includes(query) || gruppe?.bezeichnung?.toLowerCase().includes(query); - }).map(gruppe => ({ - label: gruppe.bezeichnung - ? `${gruppe.gruppe_kurzbz.trim()} (${gruppe.bezeichnung})` - : gruppe.gruppe_kurzbz.trim(), - gid: gruppe.gid, - gruppe_kurzbz: gruppe.gruppe_kurzbz.trim(), - lehrverband: gruppe.lehrverband, - })); + const query = event.query.trim(); + + if (!query) + { + this.filteredLektor = []; + return; + } + + if (query.length < 2) + { + return; + } + + if (this.abortController) + { + this.abortController.abort(); + } + + this.abortController = new AbortController(); + const signal = this.abortController.signal; + + this.$api.call(ApiGruppe.getAllSearch(query), { signal }) + .then(result => { + this.filteredGroups = result.data.map(gruppe => ({ + label: gruppe.bezeichnung + ? `${gruppe.gruppe_kurzbz.trim()} (${gruppe.bezeichnung})` + : gruppe.gruppe_kurzbz.trim(), + gid: gruppe.gid, + gruppe_kurzbz: gruppe.gruppe_kurzbz.trim(), + lehrverband: gruppe.lehrverband, + }) + )}) + .catch(this.$fhcAlert.handleSystemError) }, reload() { this.$refs.table.reloadTable(); diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index c03e7c84c..840d7b406 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -80,9 +80,6 @@ export default { sprachen_array: [], lehrform_array: [], raumtyp_array: [], - lektor_array: [], - gruppen_array: [], - benutzer_array: [], }, selectedStudiengang: '', searchbaroptions: { @@ -185,6 +182,13 @@ export default { this.$router.replace({ name: 'byStg', params: newParams }); } }, + resetStgFilter() + { + const newParams = { ...this.filter, activeFilter: 'emp' }; + delete newParams.stg; + this.selectedStudiengang = ''; + this.$router.replace({ name: 'byEmp', params: newParams }); + }, searchfunction(params) { return this.$api.call(ApiSearchbar.search(params)); }, @@ -227,24 +231,6 @@ export default { this.dropdowns.lehrfunktion_array = result.data; }) .catch(this.$fhcAlert.handleSystemError); - - this.$api.call(ApiLektor.getLektoren()) - .then(result => { - this.dropdowns.lektor_array = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - this.$api.call(ApiGruppe.getAll()) - .then(result => { - this.dropdowns.gruppen_array = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - this.$api.call(ApiGruppe.getBenutzer()) - .then(result => { - this.dropdowns.benutzer_array = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); }, template: ` @@ -272,12 +258,25 @@ export default { :filter="filter" > diff --git a/public/js/components/LVVerwaltung/Lektor/Daten.js b/public/js/components/LVVerwaltung/Lektor/Daten.js index a9c7e53be..21e5dc196 100644 --- a/public/js/components/LVVerwaltung/Lektor/Daten.js +++ b/public/js/components/LVVerwaltung/Lektor/Daten.js @@ -31,6 +31,8 @@ export default{ changed: {}, internal_mitarbeiter_uid: null, filteredLektor: [], + abortController: null, + selectedLektorLabel: '' } }, computed: { @@ -63,7 +65,10 @@ export default{ this.internal_mitarbeiter_uid = newVal; if (newVal === null) + { this.data = null; + this.selectedLektorLabel = ''; + } else if (newVal !== undefined && this.lehreinheit_id !== undefined) this.getLektorData(); } @@ -99,10 +104,15 @@ export default{ return this.$api.call(ApiLektor.getLektorDaten(this.lehreinheit_id, this.internal_mitarbeiter_uid)) .then(result => { this.data = result.data; + this.selectedLektorLabel = `${this.data.nachname} ${this.data.vorname} (${this.data.mitarbeiter_uid})`, this.original = { ...this.data }; }) .catch(this.$fhcAlert.handleSystemError); }, + onLektorSelected(selectedLektor) + { + this.data.mitarbeiter_uid = selectedLektor.value.uid; + }, updateDaten() { if (!this.changedLength) @@ -139,17 +149,37 @@ export default{ }) .catch(this.$fhcAlert.handleSystemError); }, - searchLektor(event) + async searchLektor(event) { - const query = event.query.toLowerCase().trim(); - this.filteredLektor = this.dropdowns.lektor_array.filter(lektor => { - const fullName = `${lektor.vorname.toLowerCase()} ${lektor.nachname.toLowerCase()}`; - const reverseFullName = `${lektor.nachname.toLowerCase()} ${lektor.vorname.toLowerCase()}`; - return fullName.includes(query) || reverseFullName.includes(query) || lektor.uid.toLowerCase().includes(query); - }).map(lektor => ({ - label: `${lektor.nachname} ${lektor.vorname} (${lektor.uid})`, - uid: lektor.uid - })); + const query = event.query.trim(); + + if (!query) + { + this.filteredLektor = []; + return; + } + + if (query.length < 2) + { + return; + } + + if (this.abortController) + { + this.abortController.abort(); + } + + this.abortController = new AbortController(); + const signal = this.abortController.signal; + + this.$api.call(ApiLektor.getLektorenSearch(query), { signal }) + .then(result => { + this.filteredLektor = result.data.map(lektor => ({ + label: `${lektor.nachname} ${lektor.vorname} (${lektor.uid})`, + uid: lektor.uid + }) + )}) + .catch(this.$fhcAlert.handleSystemError) }, }, @@ -187,11 +217,12 @@ export default{ :disabled="data.vertrag_id !== null" :suggestions="filteredLektor" placeholder="Mitarbeiter hinzufügen" - v-model="data.mitarbeiter_uid" + v-model="selectedLektorLabel" field="label" container-class="col-3" dropdown @complete="searchLektor" + @item-select="onLektorSelected" name="lektorautocomplete" > @@ -231,7 +262,6 @@ export default{
    - - +
    + + +
    -
    - - - +
    + {{ $p.t('lehre', 'gesamtkosten') }}: + + {{ berechneteGesamtkosten }} € +
    -
    diff --git a/public/js/components/LVVerwaltung/Lektor/Table.js b/public/js/components/LVVerwaltung/Lektor/Table.js index 4e568f9db..461d782ed 100644 --- a/public/js/components/LVVerwaltung/Lektor/Table.js +++ b/public/js/components/LVVerwaltung/Lektor/Table.js @@ -3,7 +3,6 @@ import FormForm from '../../Form/Form.js'; import FormInput from '../../Form/Input.js'; import ApiLektor from "../../../api/lehrveranstaltung/lektor.js"; - export default{ name: "LVLektorTable", components: { @@ -36,7 +35,8 @@ export default{ ], showAutocomplete: false, filteredLektor: [], - selectedLektor: '' + selectedLektor: '', + abortController: null } }, computed: { @@ -163,17 +163,37 @@ export default{ }) }, - searchLektor(event) + async searchLektor(event) { - const query = event.query.toLowerCase().trim(); - this.filteredLektor = this.dropdowns.lektor_array.filter(lektor => { - const fullName = `${lektor.vorname.toLowerCase()} ${lektor.nachname.toLowerCase()}`; - const reverseFullName = `${lektor.nachname.toLowerCase()} ${lektor.vorname.toLowerCase()}`; - return fullName.includes(query) || reverseFullName.includes(query) || lektor.uid.toLowerCase().includes(query); - }).map(lektor => ({ - label: `${lektor.nachname} ${lektor.vorname} (${lektor.uid})`, - uid: lektor.uid - })); + const query = event.query.trim(); + + if (!query) + { + this.filteredLektor = []; + return; + } + + if (query.length < 2) + { + return; + } + + if (this.abortController) + { + this.abortController.abort(); + } + + this.abortController = new AbortController(); + const signal = this.abortController.signal; + + this.$api.call(ApiLektor.getLektorenSearch(query), { signal }) + .then(result => { + this.filteredLektor = result.data.map(lektor => ({ + label: `${lektor.nachname} ${lektor.vorname} (${lektor.uid})`, + uid: lektor.uid + }) + )}) + .catch(this.$fhcAlert.handleSystemError) }, addLektor() { diff --git a/public/js/components/LVVerwaltung/Lektor/Vertrag.js b/public/js/components/LVVerwaltung/Lektor/Vertrag.js index 095e92f7c..2c30a41c6 100644 --- a/public/js/components/LVVerwaltung/Lektor/Vertrag.js +++ b/public/js/components/LVVerwaltung/Lektor/Vertrag.js @@ -113,23 +113,16 @@ export default{
    {{$p.t('lehre', 'vertragsdetails')}} - {{ data === null ? ' – Noch kein Vertrag' : '' }} - + {{ data?.vertrag === null ? ' – ' + $p.t('lehre', 'keinvertrag') : '' }} +
    diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index 309960f54..73511a94f 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -663,6 +663,8 @@ export default { @click:new="showLehreinheitModal">
    From 8a1ca9a598e60b9599d53eb5bc8e62e8372d9bbe Mon Sep 17 00:00:00 2001 From: Paolo Date: Tue, 7 Oct 2025 13:33:49 +0200 Subject: [PATCH 221/312] Checked that the session is not an error in the FilterComponent --- application/libraries/FilterCmptLib.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/libraries/FilterCmptLib.php b/application/libraries/FilterCmptLib.php index f742b4ce1..b1de89245 100644 --- a/application/libraries/FilterCmptLib.php +++ b/application/libraries/FilterCmptLib.php @@ -902,7 +902,7 @@ class FilterCmptLib $filterCmptsSession = getSession(self::SESSION_NAME); // If something is present in session - if ($filterCmptsSession != null) + if (!isError($filterCmptsSession) && $filterCmptsSession != null) { // Loops in the session for all the filter components foreach ($filterCmptsSession as $filterCmpt => $filterCmptData) @@ -965,7 +965,7 @@ class FilterCmptLib $filterCmptsSession = getSession(self::SESSION_NAME); // If something is present in session - if ($filterCmptsSession != null) + if (!isError($filterCmptsSession) && $filterCmptsSession != null) { // Loops in the session for all the filter components foreach ($filterCmptsSession as $filterCmpt => $filterCmptData) @@ -1174,3 +1174,4 @@ class FilterCmptLib return $filterName; } } + From 5f57c53ed0cdb40233afcba80423d3eb58947841 Mon Sep 17 00:00:00 2001 From: Paolo Date: Tue, 7 Oct 2025 14:47:34 +0200 Subject: [PATCH 222/312] Api.js: better error handling --- public/js/plugins/Api.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/js/plugins/Api.js b/public/js/plugins/Api.js index 0b8e2fd17..895ecf70d 100644 --- a/public/js/plugins/Api.js +++ b/public/js/plugins/Api.js @@ -328,6 +328,7 @@ export default { url: error.request.responseURL }]; } else { + if (error.response.data.errors == undefined) return []; return error.response.data.errors; } } else if (error.request) { @@ -346,6 +347,9 @@ export default { function popHandleableErrors(errorHandling, errors) { const result = {}; const copy = []; + + if (errors == undefined) return {}; + while (errors.length) copy.push(errors.pop()); for (var error of copy) { @@ -551,4 +555,4 @@ export default { app.provide('$api', app.config.globalProperties.$api); } -}; \ No newline at end of file +}; From b681ca29c17929bd7bfd85dbde11444d99234261 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 7 Oct 2025 15:13:07 +0200 Subject: [PATCH 223/312] add info alert for heimatadresse, tune duplicate zustellkontakt handling --- .../api/frontend/v1/ProfilUpdate.php | 12 +++-- .../models/person/Profil_update_model.php | 1 + public/js/components/Cis/Profil/Profil.js | 20 +++++--- .../Cis/Profil/ProfilComponents/Adresse.js | 2 +- .../EditProfilComponents/EditAdresse.js | 6 +-- .../EditProfilComponents/EditKontakt.js | 29 +++++------ .../Profil/ProfilModal/EditProfilSelect.js | 22 +++++++-- system/phrasesupdate.php | 48 +++++++++++++++++-- 8 files changed, 103 insertions(+), 37 deletions(-) diff --git a/application/controllers/api/frontend/v1/ProfilUpdate.php b/application/controllers/api/frontend/v1/ProfilUpdate.php index d0eb26858..e5129b678 100644 --- a/application/controllers/api/frontend/v1/ProfilUpdate.php +++ b/application/controllers/api/frontend/v1/ProfilUpdate.php @@ -873,7 +873,7 @@ class ProfilUpdate extends FHCAPI_Controller $insert_kontakt_id = $insertID; $insert_kontakt_id = $this->getDataOrTerminateWithError($insert_kontakt_id, $this->p->t('profilUpdate', 'profilUpdate_insertKontakt_error')); if ($insert_kontakt_id) { - $this->handleDupplicateZustellKontakte($requested_change['zustellung'], $insert_kontakt_id, $personID); + $this->handleDupplicateZustellKontakte($requested_change['zustellung'], $insert_kontakt_id, $requested_change['kontakttyp'], $personID); } } //! DELETE @@ -890,7 +890,7 @@ class ProfilUpdate extends FHCAPI_Controller $update_kontakt_id = $this->KontaktModel->update($kontakt_id, $requested_change); $update_kontakt_id = $this->getDataOrTerminateWithError($update_kontakt_id, $this->p->t('profilUpdate', 'profilUpdate_updateKontakt_error')); if ($update_kontakt_id) { - $this->handleDupplicateZustellKontakte($requested_change['zustellung'], $update_kontakt_id, $personID); + $this->handleDupplicateZustellKontakte($requested_change['zustellung'], $update_kontakt_id, $requested_change['kontakttyp'], $personID); } } return isset($insertID) ? $insertID : null; @@ -925,12 +925,16 @@ class ProfilUpdate extends FHCAPI_Controller } } - private function handleDupplicateZustellKontakte($zustellung, $kontakt_id, $person_id) + private function handleDupplicateZustellKontakte($zustellung, $kontakt_id, $kontakttyp, $person_id) { if ($zustellung) { $this->PersonModel->addSelect("public.tbl_kontakt.kontakt_id"); $this->PersonModel->addJoin("public.tbl_kontakt", "public.tbl_kontakt.person_id = public.tbl_person.person_id"); - $zustellKontakteArray = $this->PersonModel->loadWhere(["public.tbl_person.person_id" => $person_id, "zustellung" => TRUE]); + $zustellKontakteArray = $this->PersonModel->loadWhere([ + "public.tbl_person.person_id" => $person_id, + "zustellung" => TRUE, + "kontakttyp" => $kontakttyp + ]); if (!isSuccess($zustellKontakteArray)) { return error($this->p->t('profilUpdate', 'profilUpdate_loadingZustellkontakte_error')); } diff --git a/application/models/person/Profil_update_model.php b/application/models/person/Profil_update_model.php index 141044a2c..36f53ff75 100644 --- a/application/models/person/Profil_update_model.php +++ b/application/models/person/Profil_update_model.php @@ -63,6 +63,7 @@ class Profil_update_model extends DB_Model $this->addSelect(["public.tbl_profil_update.*", "public.tbl_person.vorname"]); $this->addJoin("public.tbl_benutzer", "public.tbl_benutzer.uid = public.tbl_profil_update.uid"); $this->addJoin("public.tbl_person", "public.tbl_person.person_id = public.tbl_benutzer.person_id"); + $this->db->order_by('COALESCE(public.tbl_profil_update.updateamum, public.tbl_profil_update.insertamum)', 'DESC', false); $res = $this->loadWhere($whereClause); if (isError($res)) { return $res; diff --git a/public/js/components/Cis/Profil/Profil.js b/public/js/components/Cis/Profil/Profil.js index 8ca2cfe8e..da205816d 100644 --- a/public/js/components/Cis/Profil/Profil.js +++ b/public/js/components/Cis/Profil/Profil.js @@ -120,10 +120,10 @@ export const Profil = { }, sortProfilUpdates: (ele1, ele2) => { let result = 0; - if (ele1.status === "pending") { + if (ele1.status.toLowerCase() === "pending") { result = -1; - } else if (ele1.status === "accepted") { - result = ele2.status === "rejected" ? -1 : 1; + } else if (ele1.status.toLowerCase() === "accepted") { + result = ele2.status.toLowerCase() === "rejected" ? -1 : 1; } else { result = 1; } @@ -226,10 +226,13 @@ export const Profil = { kontakteArray = kontakteArray.concat( this.data.profilUpdates .filter((update) => { - return update.requested_change.zustellung; + return update.status === 'Pending' && update.requested_change.zustellung; }) .map((kontant) => { - return kontant.requested_change.kontakt_id; + return { + kontakt_id: kontant.requested_change.kontakt_id, + kontakttyp: kontant.requested_change.kontakttyp + }; }) ); } @@ -241,7 +244,7 @@ export const Profil = { .every((kontakt) => this.data.profilUpdates.some( (update) => - update.requested_change.kontakt_id == kontakt.kontakt_id + update.status === 'Pending' && update.requested_change.kontakt_id == kontakt.kontakt_id ) ) ) { @@ -251,7 +254,10 @@ export const Profil = { return kontakt.zustellung; }) .map((kon) => { - return kon.kontakt_id; + return { + kontakt_id: kon.kontakt_id, + kontakttyp: kon.kontakttyp + }; }) ); } diff --git a/public/js/components/Cis/Profil/ProfilComponents/Adresse.js b/public/js/components/Cis/Profil/ProfilComponents/Adresse.js index 5ba2f1fe7..7ec1213e2 100644 --- a/public/js/components/Cis/Profil/ProfilComponents/Adresse.js +++ b/public/js/components/Cis/Profil/ProfilComponents/Adresse.js @@ -4,7 +4,7 @@ export default { view:String, withZustelladresse:{ type:Boolean, - default:false, + default:true, }, }, data(){ diff --git a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js index 76e08b974..8f0724f9c 100644 --- a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js +++ b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditAdresse.js @@ -160,10 +160,8 @@ export default {
    -
    -
    +
    {{$p.t('profilUpdate','zustell_adressen_warning')}} -
    @@ -173,7 +171,7 @@ export default {
    diff --git a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditKontakt.js b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditKontakt.js index 4f978f636..b484c9dd5 100644 --- a/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditKontakt.js +++ b/public/js/components/Cis/Profil/ProfilModal/EditProfilComponents/EditKontakt.js @@ -30,12 +30,15 @@ export default { // if the kontakt is already a zustellungskontakt when the user is editing the kontakt, then no warning is shown and the zustellung will be overwritten if (JSON.parse(this.originalValue).zustellung) { return false; - } + } + const kontakteOfSelectedType = this.zustellKontakteCount.filter((element) => { + return element.kontakttyp === this.data?.kontakttyp + }); // if zustellKontakteCount is not 0 and the own kontakt has the flag zustellung set to true - if (!this.zustellKontakteCount.includes(this.data.kontakt_id)) { - return this.data.zustellung && this.zustellKontakteCount.length; + if (!this.zustellKontakteCount.some((element) => element.kontakt_id === this.data.kontakt_id)) { + return this.data.zustellung && kontakteOfSelectedType.length; } - return this.zustellKontakteCount.length >= 2 && this.data.zustellung; + return this.kontakteOfSelectedType.length >= 2 && this.data.zustellung; }, isChanged: function () { //? returns true if the original passed data object was changed @@ -57,16 +60,6 @@ export default {
    - -
    -
    -
    - {{$p.t('profilUpdate','zustell_kontakte_warning')}} -
    -
    -
    - -
    @@ -108,6 +101,14 @@ export default {
    + +
    +
    + {{$p.t('profilUpdate','zustell_kontakte_warning')}} +
    +
    + +
    {{$p.t('profilUpdate','zustellungsKontakt')}} diff --git a/public/js/components/Cis/Profil/ProfilModal/EditProfilSelect.js b/public/js/components/Cis/Profil/ProfilModal/EditProfilSelect.js index 60dd759b3..921758ae0 100644 --- a/public/js/components/Cis/Profil/ProfilModal/EditProfilSelect.js +++ b/public/js/components/Cis/Profil/ProfilModal/EditProfilSelect.js @@ -148,16 +148,32 @@ export default {