Compare commits

..

100 Commits

Author SHA1 Message Date
Johann Hoffmann 66e1e6ad8a studierendenbereich phrasenkey change so it gets inserted as a new phrase and not ignored like our phrasesupdate.php tends to do 2026-06-01 15:59:38 +02:00
Johann Hoffmann d0d590ff89 block the change of paabgabetyp if the termin has an upload associated (abgabedatum !== null) or has a note; TODO: block the same in multiedit! 2026-06-01 12:56:55 +02:00
Johann Hoffmann 60e36bb4b9 change updateProjektarbeit call so that the abgabedatum actually only gets set when doing the endupload and not also when zusatzdaten are being edited, which can certainly be an earlier or even later date; 2026-06-01 11:30:36 +02:00
Johann Hoffmann ae71517ceb expanded title validation in frontend aswell before validation for the same patterns in backend just in case; phrasen alert messages & use backend response instead of frontend validated title 2026-05-29 11:47:46 +02:00
Johann Hoffmann 9f462fe3d6 "hasContent" guardrail in abgabetoolJob to avoid sending empty email templates when every possible relevant change has been filtered by the job; 2026-05-29 10:21:44 +02:00
Johann Hoffmann 004bcc43c3 merged stv saveProjektarbeit/saveBetreuer buttons; benoten button for betreuer is now a table action button; remove viewData.uid and call student_uid via authinfo endpoint; digital signature info links in student abgabetool; show projektarbeit note in overview inside the accordions content aswell as flex-end in the header; smaller vertical padding on all abgabetool modals from 3rem to 1.5 rem aka py-5 to py-4; 2026-05-29 09:50:37 +02:00
Johann Hoffmann 6d79288f33 Merge remote-tracking branch 'origin/master' into feature-76657/AbgabetoolFinetuning
# Conflicts:
#	system/phrasesupdate.php
2026-05-27 16:11:33 +02:00
Johann Hoffmann 2eb2c36d5b replace + in email helpers with spaces; configs for title edit, multi edit table & confetti on endupload; empty student projektarbeiten check; only show dropdown labels on large displays; new phrasen & phrasen fixes; 2026-05-26 17:06:39 +02:00
Johann Hoffmann 22eed92c86 Merge branch 'feature-75838/UXImproveProjektarbeitSTVSprint247' into feature-76657/AbgabetoolFinetuning 2026-05-26 12:58:57 +02:00
Johann Hoffmann 557e43e19c extracted date handling with luxon into dateUtils file and only work with those functions to completely avoid anymore timezone bugs due to js Dates. formatDate was still affected by timezone issues and was showing off by one dates in rare cases. 2026-05-22 11:17:37 +02:00
Johann Hoffmann 790568e1e0 timezone save date format function in student detail view via getUTCDate() instead of getDate() 2026-05-22 09:55:25 +02:00
Johann Hoffmann dfc3096587 rewrote findlatestTerminWithUpload sort to guarantee sorting by abgabedatum DESC 2026-05-20 13:52:32 +02:00
Johann Hoffmann c0192e9e00 adjusted flatTable index to paabgabe_id since projektarbeit_id is not unique per row there; fixed splitMailHelpers call in Mitarbeiter page; emailHelper changes so it actually works with bcc aswell; 2026-05-18 10:23:41 +02:00
Harald Bamberger cb7a0f7669 Merge branch 'feature-70376/Lohnguide' 2026-05-13 11:53:14 +02:00
Harald Bamberger 68d97a5e97 handle case where old value or new value and not both are null explicitly in markDirty Method 2026-05-13 11:42:25 +02:00
Harald Bamberger d27071528f revert change to comparision in markDirty Method 2026-05-13 11:16:18 +02:00
Harald Bamberger 17772c3738 Merge branch 'master' into feature-70376/Lohnguide 2026-05-13 11:15:07 +02:00
Johann Hoffmann 9b823dbaa1 scope the zoom media query to abgabetool document selector to avoid spillover to CIS4 pages unintentionally; added noten headerfilter & persistence in flat table; 2026-05-08 12:44:07 +02:00
Johann Hoffmann 398e3aa139 block deletion of benotet qulity gates; sync filter clear & semester dropdown; setSelecetedFields for flat table; WIP testing & refinement of everything; 2026-05-07 16:18:58 +02:00
Johann Hoffmann 553a33aa42 adapt Betreuer Page with changes of assistenzpage; set select all checkbox flag in selectAll handler to always be in sync with selectedData; 2026-05-06 18:54:19 +02:00
Johann Hoffmann 05e9d948ea deselect filtered rows; 2026-05-06 17:18:35 +02:00
Johann Hoffmann a5d343268f fix deselect condition on filtered datasets; added new phrases for confirm & edit dialog; fixed emailSplitter phrasen & confirm; fixed modal fullscreen button alignment & color; 2026-05-06 16:49:35 +02:00
Harald Bamberger bbb4f8a01c Merge branch 'bug-76146/studvw_karteireiter_dokumente_akzeptiert_eintraege_ohne_vorhandenes_dokument' 2026-05-06 16:13:50 +02:00
Johann Hoffmann 9e061de9bd fix deselect condition on filtered datasets; added new phrases for confirm & edit dialog; 2026-05-06 15:26:02 +02:00
Johann Hoffmann d0adf2dfc3 improved assistenz subqueries for zweitbetreuer infos by using common table expression instead of 8 subqueries; adapted splitMailsHelper function to take in body parameter to set default email text by parameter; dateStyles adapted so "in 12 days" also applies to termine without uploads; titleEdit modal in student & studentDetail component; send email to relevant assistenzen & projektarbeit betreuer about change from old to new title; 2nd flat table in AbgabetoolAssistenz that provides a list of all projektarbeittermine so it can be filtered & multiselected; multi delete & multi edit on these selected termine; tried to introduce a media query for zoomed in desktop users that shrinks fontsize and tabulator rows/cells; standard assistenz table column definitions have sensible minWidths & most columns are default invisible; fancy multiselect headerfilter on qualitygate 1/2 status column; actually figured out a vue watcher race codnition triggering loadProjektarbeiten twice unnecessarily; added a reload Button in case one observes a faulty reactivity somewhere in the table; fancy multiselect headerfilter on termin status column for flat table; Preselect current Semester & autoapply Filter for it; WIP refine new table & hunt for bugz; WIP working on the exact custom select handler Handling with filtered datasets; fixed root element style on legacy php view for abgabetool in old cis; WIP define more accurate allowed to delete & allowed to Edit conditions for abgabetermine -> currently benotet quality gates can be edited/deleted!!!!! 2026-05-06 13:46:26 +02:00
Harald Bamberger fdbb93a5c5 bugfix download booking receipt failed. only fetch oehbeitrag from bis.tbl_oehbeitrag if a user is logged in 2026-05-05 14:36:07 +02:00
Harald Bamberger b7e48633ab Merge branch 'master' into bug-76146/studvw_karteireiter_dokumente_akzeptiert_eintraege_ohne_vorhandenes_dokument 2026-05-05 13:33:38 +02:00
Harald Bamberger 04dc1eb07b Merge branch 'bug-76519/StudVW_Messages_Table_column_Stati' 2026-05-05 13:04:58 +02:00
Harald Bamberger 50b229090b prefetch phrases and then render filter component instead of redrawing the table 2026-05-05 13:04:11 +02:00
Harald Bamberger 2d27a998c4 Merge branch 'bug-76109/VVW_Details_und_Status_not_Loading' 2026-05-05 11:17:49 +02:00
Harald Bamberger 090e535466 add header filters, increase height of tables, use correct category for phrase lehreinheit_id 2026-05-05 11:04:48 +02:00
Harald Bamberger c4d35181db Merge branch 'master' into bug-76109/VVW_Details_und_Status_not_Loading 2026-05-05 09:01:49 +02:00
Harald Bamberger 0ac6ef4599 Merge branch 'feature-62607/konto_oh_beitrag_betrag_aus_eigener_tabelle' 2026-05-05 08:46:22 +02:00
Werner Masik 7f13c128f1 allow null value for vordienstzeit; changed comparison in markDirty to !== (because of 0 vs. null issue) 2026-05-04 20:35:51 +02:00
Harald Bamberger cb60ddcc94 Merge branch 'master' into feature-62607/konto_oh_beitrag_betrag_aus_eigener_tabelle 2026-05-04 15:41:52 +02:00
Harald Bamberger bd4ced9559 bugfix: comma as decimal separator prevents saving booking, bugfix messages tinymce not resizeable 2026-05-04 14:39:19 +02:00
Andreas Österreicher a04d2acb86 Fixed Blank on Phrase for Abgabetool 2026-05-04 10:49:33 +02:00
Harald Bamberger de2aabf00b readd dokument preview link to api response 2026-05-04 09:30:16 +02:00
ma0068 685fc69e5d update css and add provisional height 2026-04-30 18:02:38 +02:00
Werner Masik 58a921b500 changed lohnguide kommentar data type to text 2026-04-30 09:47:12 +02:00
Johann Hoffmann f51d0f72c6 setSelectedFields() method in filter cmpt to keep custom persistence with visible fields; distinct titles assistenz/betreuer page; fix select logic to respect the possibility of dataset being filtered; beurteilungsnotiz required if note of that termin is negative; multiselect filter on qgate status columns + persistence of those; WIP switching mode to flat table showing all abgabetermine of all projektarbeiten to multi edit/delete/whatever with them selected 2026-04-28 17:27:19 +02:00
Johann Hoffmann c62c3c23f6 minimal column setup by default, added sensible minWidths 2026-04-27 16:37:36 +02:00
Johann Hoffmann b22375bd21 Merge remote-tracking branch 'origin/feature-61164/AbgabetoolQualityGates' into feature-76657/AbgabetoolFinetuning
# Conflicts:
#	public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
2026-04-27 16:24:06 +02:00
Johann Hoffmann f9550c99bb remove newAbgabetermin from SpeedDial Items 2026-04-27 16:13:31 +02:00
Johann Hoffmann df369c7149 Merge branch 'feature-75996/AbgabetoolFeedbackSprint247' into feature-76657/AbgabetoolFinetuning 2026-04-27 16:11:11 +02:00
Andreas Österreicher 552faefa51 Merge branch 'feature-76108/microdegree_abschlussurkunde' 2026-04-27 08:55:08 +02:00
Harald Bamberger 954397f028 Merge branch 'feature-70376/Lohnguide' 2026-04-22 18:52:51 +02:00
Harald Bamberger 80faa61c91 Merge branch 'master' into feature-70376/Lohnguide 2026-04-22 18:46:57 +02:00
Werner Masik 961ede66a9 lohnguide db update changed 2026-04-22 18:40:22 +02:00
Andreas Österreicher 6fec8382b5 Merge branch 'feature-76554/Personalmeldung_alt_bei_Lehre_nicht_melderelevant_rausfiltern' 2026-04-22 09:15:23 +02:00
Andreas Österreicher 4eb076d115 Fixed Saving of Entwicklungsteam 2026-04-21 17:34:36 +02:00
Harald Bamberger 7427aa87ea Merge branch 'feature-76545/findAbgabenNewOrUpdatedSinceByAbgabedatumFixTimestampDateComparison' 2026-04-21 17:01:04 +02:00
Johann Hoffmann 85043e57db added missing parenthesis 2026-04-21 17:00:07 +02:00
Johann Hoffmann 5beddbccb4 changed the where clause to a simpler = CURRENT_DATE comparison -> works the same if the job runs daily and fetches updates of one day/date; has to be changed back in case we want to find updates in a range larger than 1 day in an interval larger than 1 day; 2026-04-21 14:18:45 +02:00
kindlm e2ae9b88c8 Merge remote-tracking branch 'origin/master' 2026-04-21 12:56:30 +02:00
kindlm ca3abf9154 Small Style-Fix in Testtool
To make headings stand out more clearly from the buttons
2026-04-21 12:56:02 +02:00
Alexei Karpenko f863c6d728 personalmeldung legacy system: melderelevant is checked for lehre, bugfix: lehre is correctly added if studiengang already has sws 2026-04-20 18:24:30 +02:00
Harald Bamberger 92a2053b42 Merge branch 'feature-40870/BUG_Studstatus_unpause_order_should_be_DESC' 2026-04-20 17:30:14 +02:00
kindlm 70602be54e SaveSort und Filter-Reset in RaumÜbersicht 2026-04-20 11:30:17 +02:00
kindlm dac71f597a Spalte Anmeldedatum in RT-Übersicht
Spalte Frage_ID in RT-Administration
JQuery und Tabelsorter aus Include in Service_Uebersicht
2026-04-20 11:24:59 +02:00
Johann Hoffmann 3a646ffe77 adapt AbgabeJob relevant queries so they compare with the pgsql date function CURRENT_DATE instead of NOW() to avoid the cutoff from uploads the happened "yesterday but more than 24 hours ago"; also added another "abgabedatum < CURRENT_DATE" condition, to avoid sending this exact case as updates 2 days in a row; 2026-04-20 11:06:06 +02:00
ma0068 26db4a5e7a adding redraw tabulator and fallback to avoid empty column 2026-04-20 09:31:52 +02:00
Harald Bamberger 98a10a2f55 Merge branch 'feature-69389/AbmeldungSTGL_Anzeige_mit_Studiengangskuerzel' 2026-04-17 12:37:49 +02:00
Harald Bamberger e48b94b858 studiengangskuerzel statt kurzbzlang 2026-04-17 12:35:55 +02:00
Andreas Österreicher 0ff29ba6af Merge branch 'epic-56039/LV-Evaluierung' 2026-04-16 13:22:40 +02:00
Harald Bamberger ba543448ae Merge branch 'bug-76260/StudVW_Messages_TinyMCE_Cursor_Jumping_TextInput' 2026-04-16 11:26:32 +02:00
Harald Bamberger f121f9b5a2 deactivate pagination - since potentially msg threads are not shown completely 2026-04-16 11:23:57 +02:00
Harald Bamberger 88b22f5490 revert to v-if to not render element when not necessary and use Vue.nextTick for dependent code 2026-04-15 17:02:58 +02:00
Harald Bamberger 4b7ee9abe1 Merge branch 'feature-70376/Lohnguide' 2026-04-15 15:52:12 +02:00
Cristina d499619cf3 Added phrase endedatumMussInZukunftLiegen 2026-04-15 13:39:06 +02:00
Cristina f489153ff3 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-04-15 12:25:05 +02:00
Cristina 9b79a07fa2 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-04-15 12:24:44 +02:00
Harald Bamberger 6ce14a25d7 Merge branch 'master' into feature-70376/Lohnguide 2026-04-15 11:49:26 +02:00
Werner Masik c701d92779 fix db_update 2026-04-15 11:15:10 +02:00
Werner Masik 73e03ba901 Gehaltstyp Überstundenpauschale und Sachbezug PKW 2026-04-15 10:44:03 +02:00
ma0068 95a7797ae9 delete unused apicall for mode modal
add editor.setContent to function getReplyData
readd loadReplyData and use v-show for visibleDiv for mode inSamePage
2026-04-14 15:46:34 +02:00
ma0048 3ce3eff022 fehlendes mapping hinzugefuegt 2026-04-14 09:30:45 +02:00
ma0048 21d80905a2 akzeptierte dokumente anzeigen, auch wenn kein dokument vorhanden ist 2026-04-13 13:04:46 +02:00
ma0048 ea0a249612 micro degree abschlussdokumente hinzugefuegt 2026-04-13 09:14:27 +02:00
ma0068 843894405e changes for NewDiv
remove Watcher for formData Fields
add predefault settings for tabulator fields
add setContent for Editor
change text for closing window/tab
show alertSuccess for sending Message just in case of inSamePage
2026-04-10 13:03:24 +02:00
ma0068 8fddbc3a32 delete watcher for formData fields, add setContent for loading Vorlage 2026-04-10 09:15:34 +02:00
Johann Hoffmann 6868e68145 define datepickers to use ISO strings internally with model-type="yyyy-MM-dd" to avoid construction of new js dateobjects when deleting the value via text input, which could lead to a off by one day/timezone error when saving the date 2026-04-09 15:42:05 +02:00
Johann Hoffmann 44e43d9479 phrasenkey fix 'c4fehlerAktualitaetProjektarbeit ' => 'c4fehlerAktualitaetProjektarbeit' 2026-04-08 15:47:17 +02:00
Werner Masik 5c463c0866 add vordienstzeit to lohnguide 2026-04-06 22:25:13 +02:00
Werner Masik 423bbd95a6 add vordienstzeit to lohnguide 2026-04-06 22:25:12 +02:00
ma0068 298dbbf400 use context vm for tabulator event, refactor function toggleRowClick, delete unused commented sections 2026-04-02 10:58:49 +02:00
Harald Bamberger 627a52e3d1 Merge branch 'master' into feature-70376/Lohnguide 2026-03-25 09:36:27 +01:00
Johann Hoffmann 079a8e3ec1 added selected: x | filtered: y | total: z count to assistenz table 2026-03-24 15:19:36 +01:00
Johann Hoffmann 6ddb064c9e qgateStatus tooltips 2026-03-23 10:09:46 +01:00
Johann Hoffmann 9b8b1b2532 cellFormatter & TitleFormatter breakpoints at 100px to display longform or shortform text; phrasenupdate; lefthandside formatting per default; curated tabulator tooltips for complex cells WIP 2026-03-20 12:21:47 +01:00
Johann Hoffmann aba4bc2909 merge projektarbeit details & betreuer form and handle several UX changes regarding formData of both 2026-03-19 11:12:14 +01:00
Harald Bamberger 059b13938e Merge branch 'master' into feature-70376/Lohnguide 2026-03-18 11:46:27 +01:00
Johann Hoffmann f6d2b55f75 redrawTableScrollSave method instead of manual reset 2026-03-16 11:26:31 +01:00
Werner Masik 6b816def31 add lohnguide to vertragsbestandteil SQL 2026-03-05 15:34:51 +01:00
Werner Masik 5fbcf588ed fix vertragsbestandteil lohnguide 2026-03-05 14:29:56 +01:00
Werner Masik 41b2a6d1d4 added db migration for lohnguide 2026-03-04 10:53:30 +01:00
Werner Masik e054f1222b basic model and factory for lohnguide 2026-03-03 11:50:45 +01:00
ma0068 38e8f91fdf add kurzbzlang to studentDropdown suggestion 2025-11-25 10:48:57 +01:00
ma0048 ba6224bc78 oeh betrag aus der eigener tabelle holen
studentenverwaltung bei jedem studiensemester wechsel
fas nur einmalig ueber die variable
2025-09-02 11:18:24 +02:00
cgfhtw d542cf7720 s&d 2024-08-14 16:20:47 +02:00
142 changed files with 4878 additions and 7529 deletions
+11
View File
@@ -38,8 +38,19 @@ $config['SIGNATUR_CHECK_PAABGABETYPEN'] = ['end'];
// to be used as "https://moodle.technikum-wien.at/course/view.php?idnumber=dl{$stg_kz}" for stg specific moodle routing
$config['STG_MOODLE_LINK'] = 'https://moodle.technikum-wien.at/course/view.php?idnumber=dl';
// TODO: check if these links change if the file changes and how to better retrieve the link?
$config['SIGNATUR_INFO_LINK_GERMAN'] = 'https://cis.technikum-wien.at/cms/dms.php?id=214779';
$config['SIGNATUR_INFO_LINK_ENGLISH'] = 'https://cis.technikum-wien.at/cms/dms.php?id=264256';
$config['ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'] = true;
$config['ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'] = true;
$config['BETREUER_SAMMELMAIL_BUTTON_STUDENT'] = true;
$config['MULTIEDIT_TABLE'] = true;
$config['STUDENT_EDIT_PROJEKTARBEIT_TITLE'] = true;
$config['CONFETTI_ON_ENDUPLOAD'] = true;
-1
View File
@@ -63,7 +63,6 @@ $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/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
@@ -1,51 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Benotungstool extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => self::PERM_LOGGED
]);
$this->_ci =& get_instance();
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
// TODO: check if related CIS config is also loaded when being routed in Cis4 by vuerouter
// TODO: check if new benotungstool should be configurable the exact same way?
$viewData = array(
'uid'=>getAuthUID(),
'CIS_GESAMTNOTE_UEBERSCHREIBEN' => CIS_GESAMTNOTE_UEBERSCHREIBEN,
'CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF' => CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF,
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN3' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN3,
'CIS_GESAMTNOTE_PRUEFUNG_TERMIN2' => CIS_GESAMTNOTE_PRUEFUNG_TERMIN2,
'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE,
'CIS_GESAMTNOTE_PUNKTE' => CIS_GESAMTNOTE_PUNKTE,
'CIS_GESAMTNOTE_GEWICHTUNG' => CIS_GESAMTNOTE_GEWICHTUNG,
'CIS_ANWESENHEITSLISTE_NOTENLISTE_ANZEIGEN' => CIS_ANWESENHEITSLISTE_NOTENLISTE_ANZEIGEN
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']);
}
}
@@ -35,9 +35,12 @@ class Abgabe extends FHCAPI_Controller
'getStudentProjektabgaben' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw', 'basis/abgabe_lektor:rw'),
'postStudentProjektarbeitZwischenabgabe' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw'),
'postStudentProjektarbeitEndupload' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw'),
'postStudentProjektarbeitTitel' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_student:rw'),
'getMitarbeiterProjektarbeiten' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'postProjektarbeitAbgabe' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'patchProjektarbeitAbgabeMultiple' => array('basis/abgabe_assistenz:rw'),
'deleteProjektarbeitAbgabe' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'deleteProjektarbeitAbgabeMultiple' => array('basis/abgabe_assistenz:rw'),
'postSerientermin' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'fetchDeadlines' => array('basis/abgabe_assistenz:rw', 'basis/abgabe_lektor:rw'),
'getPaAbgabetypen' => self::PERM_LOGGED,
@@ -90,6 +93,7 @@ class Abgabe extends FHCAPI_Controller
$ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT');
$ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER');
$BETREUER_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('BETREUER_SAMMELMAIL_BUTTON_STUDENT');
$MULTIEDIT_TABLE = $this->config->item('MULTIEDIT_TABLE');
$ret = array(
'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link,
@@ -97,7 +101,7 @@ class Abgabe extends FHCAPI_Controller
'abgabetypenBetreuer' => $abgabetypenBetreuer,
'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT,
'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER,
'BETREUER_SAMMELMAIL_BUTTON_STUDENT' => $BETREUER_SAMMELMAIL_BUTTON_STUDENT,
'MULTIEDIT_TABLE' => $MULTIEDIT_TABLE,
);
$this->terminateWithSuccess($ret);
@@ -107,10 +111,18 @@ class Abgabe extends FHCAPI_Controller
* loads config related to abgabetool for students to avoid handing out links reserved for employees
*/
public function getConfigStudent() {
$moodle_link =$this->config->item('STG_MOODLE_LINK');
$moodle_link = $this->config->item('STG_MOODLE_LINK');
$title_edit_allowed = $this->config->item('STUDENT_EDIT_PROJEKTARBEIT_TITLE');
$confetti_on_endupload = $this->config->item('CONFETTI_ON_ENDUPLOAD');
$siginfolink_german = $this->config->item('SIGNATUR_INFO_LINK_GERMAN');
$siginfolink_english = $this->config->item('SIGNATUR_INFO_LINK_ENGLISH');
$ret = array(
'moodle_link' => $moodle_link,
'title_edit_allowed' => $title_edit_allowed,
'confetti_on_endupload' => $confetti_on_endupload,
'siginfolink_german' => $siginfolink_german,
'siginfolink_english' => $siginfolink_english
);
$this->terminateWithSuccess($ret);
@@ -187,8 +199,8 @@ class Abgabe extends FHCAPI_Controller
} else {
$result = $this->ProjektarbeitModel->getStudentProjektarbeitenWithBetreuer(getAuthUID());
}
$projektarbeiten = getData($result);
$projektarbeiten = hasData($result) ? getData($result) : array();
if(count($projektarbeiten)) {
foreach($projektarbeiten as $pa) {
@@ -410,9 +422,16 @@ class Abgabe extends FHCAPI_Controller
$this->checkAbgabeSignatur($paabgabe, $projektarbeit->student_uid);
$signaturstatus = $paabgabe->signatur;
// update projektarbeit cols
$this->ProjektarbeitModel->updateProjektarbeit($projektarbeit_id, $sprache, $abstract, $abstract_en
, $schlagwoerter, $schlagwoerter_en, $seitenanzahl);
// update projektarbeit cols with zusatzdaten AND abgabedatum!
$this->ProjektarbeitModel->update($projektarbeit->projektarbeit_id, array(
'sprache' => $sprache,
'seitenanzahl' => $seitenanzahl,
'abgabedatum' => date('Y-m-d'),
'schlagwoerter_en' => $schlagwoerter_en,
'schlagwoerter' => $schlagwoerter,
'abstract' => $abstract,
'abstract_en' => $abstract_en
));
// update paabgabe datum
@@ -448,7 +467,218 @@ class Abgabe extends FHCAPI_Controller
}
}
/**
* POST METHOD
* allows a student (or assistenz on their behalf) to update the titel of their own projektarbeit.
* blocked once the projektarbeit has been graded (checkProjektarbeitForFinishedStatus).
*/
public function postStudentProjektarbeitTitel()
{
if(!$this->config->item('STUDENT_EDIT_PROJEKTARBEIT_TITLE')) {
$this->terminateWithError($this->p->t('global', 'c4studentEditNotAllowed'), 'general');
};
$projektarbeit_id = $this->input->post('projektarbeit_id');
$titel = $this->input->post('titel');
if ($projektarbeit_id === NULL || trim((string)$projektarbeit_id) === ''
|| $titel === NULL || trim((string)$titel) === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
// strip all HTML tags to prevent XSS in mail bodies, table views and Projektarbeitsbenotung
$titel = trim(strip_tags($titel));
if ($titel === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
// Reject emojis and pictographs
// allows foreign letters, math symbols, accents, and standard punctuation.
$emojiPattern = '/[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1FAFF}\x{23E9}-\x{23EF}\x{2b50}\x{2700}-\x{27BF}]/u';
// i would like this much more but our server does not recognize this utf-8 character range this way, so hexcodes it is
// if (preg_match('/\p{Extended_Pictographic}/u', $titel)) {
if (preg_match($emojiPattern, $titel)) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
// Verify the projektarbeit actually belongs to the supplied student_uid
$res = $this->ProjektarbeitModel->getStudentInfoForProjektarbeitId($projektarbeit_id);
if (isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('abgabetool', 'c4projektarbeitNichtGefunden'), 'general');
}
$assignedStudentUid = getData($res)[0]->uid;
// A student may only update their own title; assistenz is covered by checkZuordnung
$zugeordnet = $this->checkZuordnung($projektarbeit_id, getAuthUID());
if (getAuthUID() !== $assignedStudentUid && !$zugeordnet) {
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
$data = getData($result);
$oldTitle = $data[0]->titel ?? '';
$result = $this->ProjektarbeitModel->update(
$projektarbeit_id,
array(
'titel' => $titel,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
)
);
$this->getDataOrTerminateWithError($result, 'general');
$this->logLib->logInfoDB(array(
'titelUpdate',
array(
'projektarbeit_id' => $projektarbeit_id,
'titel' => $titel,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
),
getAuthUID(),
getAuthPersonId()
));
$this->sendTitelChangedEmail(
$projektarbeit_id,
$titel,
$oldTitle,
$assignedStudentUid
);
$result = $this->ProjektarbeitModel->load($projektarbeit_id);
$titel = hasData($result) ? getData($result)[0]->titel : $titel;
$this->terminateWithSuccess($titel);
}
/**
* Notifies all betreuer of a projektarbeit and all assistenzen responsible for its studiengang
* when a student updates the titel of their projektarbeit.
*
* Betreuer retrieval mirrors AbgabetoolJob->notifyBetreuerAboutChangedAbgaben.
* Assistenz retrieval mirrors AbgabetoolJob->notifyAssistenzAboutChangedAbgaben.
*
* @param int $projektarbeit_id
* @param string $new_titel The titel as it was saved
* @param string $student_uid
*/
private function sendTitelChangedEmail($projektarbeit_id, $new_titel, $old_titel, $student_uid)
{
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
$this->load->model('education/Projektbetreuer_model', 'ProjektbetreuerModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('person/Person_model', 'PersonModel');
$studentNameResult = $this->PersonModel->getFullName($student_uid);
$studentFullName = hasData($studentNameResult) ? getData($studentNameResult) : $student_uid;
$studentInfoResult = $this->ProjektarbeitModel->getStudentInfoForProjektarbeitId($projektarbeit_id);
if (isError($studentInfoResult) || !hasData($studentInfoResult)) {
$this->logLib->logInfoDB(array('sendTitelChangedEmail: student info not found', $projektarbeit_id));
return;
}
$studentInfo = getData($studentInfoResult)[0];
$studiengang_kz = $studentInfo->studiengang_kz;
$stgResult = $this->StudiengangModel->load($studiengang_kz);
$oe_kurzbz = null;
if (!isError($stgResult) && hasData($stgResult)) {
$oe_kurzbz = getData($stgResult)[0]->oe_kurzbz ?? null;
}
// build shared mail data
$base_mail_data = array(
'studentFullName' => $studentFullName,
'new_titel' => $new_titel,
'old_titel' => $old_titel
);
// notify all betreuer
$betreuerResult = $this->ProjektbetreuerModel->getAllBetreuerOfProjektarbeit($projektarbeit_id);
if (!isError($betreuerResult) && hasData($betreuerResult)) {
$linkAbgabetool = APP_ROOT . $this->config->item('URL_MITARBEITER');
foreach (getData($betreuerResult) as $betreuer) {
$email = $betreuer->uid ? $betreuer->uid . '@' . DOMAIN : ($betreuer->private_email ?? null);
if (!$email) {
$this->logLib->logInfoDB(array('sendTitelChangedEmail: no email for betreuer', $betreuer->person_id));
continue;
}
$anredeResult = $this->ProjektarbeitModel->getProjektbetreuerAnrede($betreuer->person_id);
$anrede = (!isError($anredeResult) && hasData($anredeResult)) ? getData($anredeResult)[0] : null;
$mail_data = array_merge($base_mail_data, array(
'anredeFillString' => ($anrede->anrede ?? '') === 'Herr' ? 'r' : '',
'anrede' => $anrede->anrede ?? '',
'fullFormattedNameString' => $anrede->first ?? $email,
'linkAbgabetool' => $linkAbgabetool,
));
sendSanchoMail(
'PATitleUpdated',
$mail_data,
$email,
$this->p->t('abgabetool', 'c4PATitleChanged')
);
}
}
// notify assistenz for the studiengang OE
if (!$oe_kurzbz) {
$this->logLib->logInfoDB(array('sendTitelChangedEmail: no oe_kurzbz resolved, skipping assistenz', $studiengang_kz));
return;
}
$assistenzResult = $this->OrganisationseinheitModel->getAssistenzForOE($oe_kurzbz);
if (isError($assistenzResult) || !hasData($assistenzResult)) {
return;
}
$linkAbgabetool = APP_ROOT . $this->config->item('URL_ASSISTENZ');
// similar pattern as job uses via the assistenzMap
$sentTo = [];
foreach (getData($assistenzResult) as $assistenz) {
if (in_array($assistenz->person_id, $sentTo)) {
continue;
}
$sentTo[] = $assistenz->person_id;
$email = $assistenz->uid . '@' . DOMAIN;
$mail_data = array_merge($base_mail_data, array(
'anredeFillString' => $assistenz->anrede === 'Herr' ? 'r' : '',
'anrede' => $assistenz->anrede ?? '',
'fullFormattedNameString' => $assistenz->first ?? ($assistenz->uid . '@' . DOMAIN),
'linkAbgabetool' => $linkAbgabetool,
));
sendSanchoMail(
'PATitleUpdated',
$mail_data,
$email,
$this->p->t('abgabetool', 'c4PATitleChanged')
);
}
}
// validate paabgabe deadline against servertime just in case a student spoofs their local clock and thus
// unlocks the upload ui
private function checkPaabgabeDeadline($paabgabe_id) {
@@ -624,6 +854,16 @@ class Abgabe extends FHCAPI_Controller
$existingPaabgabeArr = getData($existingResult);
if(count($existingPaabgabeArr) > 0) $existingPaabgabe = $existingPaabgabeArr[0];
if($existingPaabgabe->note !== null || $existingPaabgabe->abgabedatum !== null) {
// check if a change of paabgabetyp is being attempted -> not allowed at this point
if($paabgabetyp_kurzbz !== $existingPaabgabe->paabgabetyp_kurzbz) {
$this->terminateWithError($this->p->t('abgabetool', 'c4abgabetypAendernNichtErlaubt'));
}
// check if a change of deadline aka datum is being attempted -> should not be allowed at this point?
}
$result = $this->PaabgabeModel->update(
$paabgabe_id,
array(
@@ -687,6 +927,99 @@ class Abgabe extends FHCAPI_Controller
$this->terminateWithSuccess([$paabgabe, $existingPaabgabe]);
}
/**
* called by abgabetool/assistenz when bulk-editing multiple abgabetermine via the flat termine table view
* only fields present in the payload are updated - absent fields are left untouched
*/
public function patchProjektarbeitAbgabeMultiple() {
$paabgabe_ids = $this->input->post('paabgabe_ids');
if ($paabgabe_ids === NULL || !is_array($paabgabe_ids) || count($paabgabe_ids) === 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
// collect only fields that were actually sent
$updateFields = [];
$datum = $this->input->post('datum');
if ($datum !== NULL && trim((string)$datum) !== '') {
$updateFields['datum'] = $datum;
}
$paabgabetyp_kurzbz = $this->input->post('paabgabetyp_kurzbz');
if ($paabgabetyp_kurzbz !== NULL && trim((string)$paabgabetyp_kurzbz) !== '') {
$updateFields['paabgabetyp_kurzbz'] = $paabgabetyp_kurzbz;
}
$kurzbz = $this->input->post('kurzbz');
if ($kurzbz !== NULL) {
$updateFields['kurzbz'] = $kurzbz;
}
// booleans: only include if explicitly posted
$upload_allowed = $this->input->post('upload_allowed');
if ($upload_allowed !== NULL) {
$updateFields['upload_allowed'] = filter_var($upload_allowed, FILTER_VALIDATE_BOOLEAN);
}
$fixtermin = $this->input->post('fixtermin');
if ($fixtermin !== NULL) {
$updateFields['fixtermin'] = filter_var($fixtermin, FILTER_VALIDATE_BOOLEAN);
}
if (empty($updateFields)) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$results = [];
foreach ($paabgabe_ids as $paabgabe_id) {
$paabgabe_id = trim((string)$paabgabe_id);
if ($paabgabe_id === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$projektarbeit_id = $this->getProjektarbeitIDForPaabgabeID($paabgabe_id);
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
$zugeordnet = $this->checkZuordnung($projektarbeit_id, getAuthUID());
if (!$zugeordnet) {
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
$paabgabeResult = $this->PaabgabeModel->load($paabgabe_id);
$paabgabeArr = $this->getDataOrTerminateWithError($paabgabeResult, 'general');
if (count($paabgabeArr) === 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$result = $this->PaabgabeModel->update(
$paabgabe_id,
array_merge($updateFields, [
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
])
);
$this->getDataOrTerminateWithError($result, 'general');
$results[] = getData($this->PaabgabeModel->load($paabgabe_id))[0];
$this->logLib->logInfoDB(array(
'paabgabe bulk updated',
$paabgabe_id,
$updateFields,
getAuthUID(),
getAuthPersonId()
));
}
$this->terminateWithSuccess($results);
}
/**
* called by abgabetool/mitarbeiter in mitarbeiterdetail.js when deleting an abgabetermin
* deletion is only possible if user is assistenz OR betreuer deletes their own custom termin
@@ -719,11 +1052,55 @@ class Abgabe extends FHCAPI_Controller
$result = $this->PaabgabeModel->delete($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result, 'general');
// TODO: consider this in nightly email job
$this->logLib->logInfoDB(array($paabgabeArr[0], getAuthUID(), getAuthPersonId()));
$this->terminateWithSuccess($result);
}
/**
* called by abgabetool/assistenz when deleting multiple abgabetermine via the flat termine table view
*/
public function deleteProjektarbeitAbgabeMultiple() {
$paabgabe_ids = $this->input->post('paabgabe_ids');
if ($paabgabe_ids === NULL || !is_array($paabgabe_ids) || count($paabgabe_ids) === 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$results = [];
foreach ($paabgabe_ids as $paabgabe_id) {
$paabgabe_id = trim((string)$paabgabe_id);
if ($paabgabe_id === '') {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$this->checkProjektarbeitForFinishedStatus($this->getProjektarbeitIDForPaabgabeID($paabgabe_id));
$zugeordnet = $this->checkZuordnungByPaabgabe($paabgabe_id, getAuthUID());
if (!$zugeordnet) {
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
$paabgabeResult = $this->PaabgabeModel->load($paabgabe_id);
$paabgabeArr = $this->getDataOrTerminateWithError($paabgabeResult, 'general');
if (count($paabgabeArr) == 0) {
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
$result = $this->PaabgabeModel->delete($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result, 'general');
$results[] = $result;
$this->logLib->logInfoDB(array($paabgabeArr[0], getAuthUID(), getAuthPersonId()));
}
$this->terminateWithSuccess($results);
}
/**
* endpoint for adding the same paabgabe for multiple projektarbeiten
* can be slow for large n since it queries twice per projektarbeit_id
@@ -1106,9 +1483,15 @@ class Abgabe extends FHCAPI_Controller
$this->terminateWithError($this->p->t('abgabetool', 'c4noZuordnungBetreuerStudent'), 'general');
}
// update projektarbeit cols
$this->ProjektarbeitModel->updateProjektarbeit($projektarbeit_id,$sprache,$abstract,$abstract_en
,$schlagwoerter, $schlagwoerter_en, $seitenanzahl);
// update projektarbeit cols with zusatzdaten only
$this->ProjektarbeitModel->update($projektarbeit_id, array(
'sprache' => $sprache,
'seitenanzahl' => $seitenanzahl,
'schlagwoerter_en' => $schlagwoerter_en,
'schlagwoerter' => $schlagwoerter,
'abstract' => $abstract,
'abstract_en' => $abstract_en
));
$this->logLib->logInfoDB(array('zusatzdatenEditMitarbeiter', array(
'updatevon' => getAuthUID(),
@@ -1206,7 +1589,7 @@ class Abgabe extends FHCAPI_Controller
};
Events::trigger('projektarbeit_is_current', $projektarbeit_id, $returnFunc);
if(!$projektarbeitIsCurrent) {
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeit'), 'general');
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeitv2'), 'general');
}
// Link to Abgabetool
@@ -1411,7 +1794,13 @@ class Abgabe extends FHCAPI_Controller
$data = getData($res)[0];
if($data->note !== NULL) {
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeit'), 'general');
// hardcode this error msg cause phrasen arent reliable and people keep bugging why the cant edit old entries they definitely shouldnt update
$message = $this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeitv2');
if(strpos($message, "<<") === 0) { // phrase could not be loaded
$this->terminateWithError('Die Projektarbeit wurde bereits benotet, Sie dürfen deshalb keine weiteren Termine anlegen oder bearbeiten.', 'general');
} else {
$this->terminateWithError($message);
}
}
}
@@ -41,9 +41,7 @@ class LvPlan extends FHCAPI_Controller
'getLehreinheitStudiensemester' => self::PERM_LOGGED,
'studiensemesterDateInterval' => self::PERM_LOGGED,
'getLvPlanForStudiensemester' => self::PERM_LOGGED,
'getLv' => self::PERM_LOGGED,
'getLeEvents' => self::PERM_LOGGED,
'getLvEvents' => self::PERM_LOGGED,
'getLv' => self::PERM_LOGGED
]);
$this->load->library('LogLib');
@@ -56,12 +54,6 @@ class LvPlan extends FHCAPI_Controller
));
$this->load->library('form_validation');
$this->load->library('PhrasesLib');
$this->loadPhrases(
array(
'ui'
)
);
}
//------------------------------------------------------------------------------------------------------------------
@@ -153,38 +145,6 @@ class LvPlan extends FHCAPI_Controller
));
}
public function getLeEvents($le_id = null, $start_date = null, $end_date = null, $stundenplan = 'stundenplandev')
{
if (is_null($le_id) || is_null($start_date) || is_null($end_date))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
if ($stundenplan !== 'stundenplandev' && $stundenplan !== 'stundenplan')
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->library('StundenplanLib');
$result = $this->stundenplanlib->getEventsByLE($le_id, $start_date, $end_date, $stundenplan);
$lvplanEvents = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($lvplanEvents);
}
public function getLvEvents($lv_id = null, $start_date = null, $end_date = null, $stundenplan = 'stundenplandev')
{
if (is_null($lv_id) || is_null($start_date) || is_null($end_date))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
if ($stundenplan !== 'stundenplandev' && $stundenplan !== 'stundenplan')
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->library('StundenplanLib');
$result = $this->stundenplanlib->getEventsByLV($lv_id, $start_date, $end_date, $stundenplan);
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
//TODO: delete this function if we don't use the old calendar export endpoints anymore
public function studiensemesterDateInterval($date){
$this->load->model('organisation/Studiensemester_model','StudiensemesterModel');
@@ -1,929 +0,0 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
use CI3_Events as Events;
class Noten extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getStudentenNoten' => array('lehre/benotungstool:rw'),
'getNoten' => array('lehre/benotungstool:rw'),
'saveStudentenNoten' => array('lehre/benotungstool:rw'),
'getNotenvorschlagStudent' => array('lehre/benotungstool:rw'),
'saveNotenvorschlag' => array('lehre/benotungstool:rw'),
'saveStudentPruefung' => array('lehre/benotungstool:rw'),
'createPruefungen' => array('lehre/benotungstool:rw'),
'saveNotenvorschlagBulk' => array('lehre/benotungstool:rw'),
'savePruefungenBulk' => array('lehre/benotungstool:rw')
]);
$this->load->library('AuthLib', null, 'AuthLib');
$this->load->library('PhrasesLib');
// Loads phrases system
$this->loadPhrases([
'global',
'person',
'benotungstool',
'lehre',
'ui'
]);
require_once(FHCPATH . 'include/mobilitaet.class.php');
$this->load->model('education/LePruefung_model', 'LePruefungModel');
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->helper('hlp_sancho_helper');
}
/**
* GET METHOD
* expects 'lv_id', 'sem_kurzbz'
* returns List of all Students of given lehrveranstaltung and semester and fetches their grades.
* Loads LvGesamtnote aswell as Teilnoten from externalSources via getExternalGrades Event.
* Calculates the Notenvorschlag for every student based on averaging their Teilnoten.
* Finally also fetches all Prüfungen for every student which are linked to lva and semester.
*/
public function getStudentenNoten() {
$lv_id = $this->input->get("lv_id",TRUE);
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
if (!isset($lv_id) || isEmptyString($lv_id)
|| !isset($sem_kurzbz) || isEmptyString($sem_kurzbz))
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
// get studenten for lva & sem with zeugnisnote if available
$studenten = $this->LehrveranstaltungModel->getStudentsByLv($sem_kurzbz, $lv_id);
$studentenData = $this->getDataOrTerminateWithError($studenten);
$func = function ($value) {
return $value->uid;
};
$grades = array();
$student_uids = array_map($func, $studentenData);
foreach($student_uids as $uid) {
$grades[$uid]['grades'] = [];
$res = $this->StudentModel->load([$uid]);
if(!isError($res) && hasData($res)) $student = getData($res)[0];
$prestudent_id = $student->prestudent_id;
// TODO: last class to get rid of but this one is complicated
$mobility = new mobilitaet();
$mobility->loadPrestudent($prestudent_id);
$output = $mobility->result;
$eintrag = '';
foreach ($output as $k)
{
if(($k->mobilitaetstyp_kurzbz == 'GS') && ($k->studiensemester_kurzbz == $sem_kurzbz))
$eintrag = ' (d.d.)';
}
$grades[$uid]['mobility'] = $eintrag;
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $uid, $sem_kurzbz);
if(!isError($result) && hasData($result)) {
$lvgesamtnote = getData($result)[0];
$grades[$uid]['note_lv'] = $lvgesamtnote->note;
$grades[$uid]['freigabedatum'] = $lvgesamtnote->freigabedatum;
$grades[$uid]['benotungsdatum'] = $lvgesamtnote->benotungsdatum;
$grades[$uid]['punkte_lv'] = $lvgesamtnote->punkte;
} else {
$grades[$uid]['note_lv'] = null;
$grades[$uid]['freigabedatum'] = null;
$grades[$uid]['benotungsdatum'] = null;
$grades[$uid]['punkte_lv'] = null;
}
}
// send $grades reference to moodle addon
Events::trigger(
'getExternalGrades',
function & () use (&$grades)
{
return $grades;
},
[
'lvid' => $lv_id,
'stsem' => $sem_kurzbz
]
);
// calculate notenvorschläge from teilnoten
foreach($studentenData as $student) {
$g = $grades[$student->uid]['grades'];
$note_lv = $grades[$student->uid]['note_lv'];
// overwrite any calculation with lv note once available
if(!is_null($note_lv)) {
$student->note_vorschlag = $note_lv;
} else if(count($g) > 0) {
$notensumme = 0;
$notensumme_gewichtet = 0;
$gewichtsumme = 0;
$punktesumme = 0;
$punktesumme_gewichtet = 0;
$anzahlnoten = 0;
foreach($g as $teilnote) {
if (is_numeric($teilnote['grade']) || (is_null($teilnote['grade']) && is_numeric($teilnote['points'])))
{
$notensumme += $teilnote['grade'];
$punktesumme += $teilnote['points'];
$notensumme_gewichtet += $teilnote['grade'] * $teilnote['weight'];
$punktesumme_gewichtet += $teilnote['points'] * $teilnote['weight'];
$gewichtsumme += $teilnote['weight'];
$anzahlnoten += 1;
}
}
// TODO: develop the punkte feature with models
// calculate grades points from notenschlüssel
if (CIS_GESAMTNOTE_PUNKTE)
{
if (defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG)
{
// Lehreinheitsgewichtung
$punkte_vorschlag = round($punktesumme_gewichtet / $gewichtsumme, 2);
$notenschluessel = new notenschluessel();
$note_vorschlag = $notenschluessel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz);
}
else
{
$punkte_vorschlag = round($punktesumme / $anzahlnoten, 2);
$notenschluessel = new notenschluessel();
$note_vorschlag = $notenschluessel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz);
}
}
else
{
if (defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG)
{
$note_vorschlag = round($notensumme_gewichtet / $gewichtsumme);
}
else
{
$note_vorschlag = round($notensumme / $anzahlnoten);
}
}
$student->note_vorschlag = $note_vorschlag;
}
}
// get all prüfungen with noten held in that semester in that lva
$pruefungen = $this->LePruefungModel->getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz);
$pruefungenData = getData($pruefungen);
$this->terminateWithSuccess(array($studentenData, $pruefungenData, DOMAIN, $grades));
}
/**
* GET METHOD
* returns List of all available & active NotenOptions
*/
public function getNoten() {
$this->load->model('education/Note_model', 'NoteModel');
$result = $this->NoteModel->getAllActive();
$noten = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($noten);
}
/**
* POST METHOD
* expects 'lv_id', 'sem_kurzbz', 'password', 'noten'
* Notenfreigabe method which checks the users password as a security measure.
* Tries to load Lehrveranstaltung, Studiengang and Person via Model in order to validate the coherency of input parameters
* lv_id & sem_kurzbz in relation to the noten array delivered.
* Updates the LvGesamtnote note, aswell as freigabedatum, which is key in the logic of the freigegeben/offen/changed notenStatus
* Along this process builds a html table to be placed in a confirmation email (uid only and full variant depending on config)
* which is being sent to the Lektor, aswell as the assigned Assistenz.
*/
public function saveStudentenNoten() {
$result = $this->getPostJSON();
if(!property_exists($result, 'sem_kurzbz') || !property_exists($result, 'lv_id') ||
!property_exists($result, 'password') || !property_exists($result, 'noten')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
if(!$this->AuthLib->checkUserAuthByUsernamePassword(getAuthUID(), $result->password)->retval) {
$this->terminateWithError($this->p->t('global', 'wrongPassword'), 'general');
}
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$ret = [];
$res = $this->LehrveranstaltungModel->load($lv_id);
if(isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('benotungstool', 'noValidLvFoundForId', [$lv_id]));
}
$lv = getData($res)[0];
$studiengang_kz = $lv->studiengang_kz;
$res = $this->StudiengangModel->load($studiengang_kz);
if(isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('benotungstool', 'noValidStudiengangFoundForId', [$studiengang_kz]));
}
$sg = getData($res)[0];
$lvaFullName = $sg->kurzbzlang . ' ' . $lv->semester . '.Semester
' . $lv->bezeichnung . " - " .$lv->lehrform_kurzbz. " " . $lv->orgform_kurzbz . " - " . $sem_kurzbz;
$emails = explode(', ', $sg->email);
$res = $this->PersonModel->load(getAuthPersonId());
if(isError($res) || !hasData($res)) {
$this->terminateWithError($this->p->t('benotungstool', 'noValidPersonFoundForId', [getAuthPersonId()]));
}
$pers = getData($res)[0];
$lektorFullName = $pers->anrede.' '.$pers->vorname.' '.$pers->nachname; //.' ('.$pers->kurzbz.')';
$res = $this->StudienplanModel->getStudienplanByLvaSemKurzbz($lv_id, $sem_kurzbz);
$data = getData($res);
$studienplan_bezeichnung = '';
foreach ($data as $row) {
$studienplan_bezeichnung .= $row->bezeichnung . ' ';
}
$betreff = $this->p->t('benotungstool','notenfreigabe').' ' . $lv->bezeichnung . ' ' . $lv->orgform_kurzbz . ' - ' . $studienplan_bezeichnung;
$studlist = "<table border='1'><tr>";
if (defined('CIS_GESAMTNOTE_FREIGABEMAIL_NOTE') && CIS_GESAMTNOTE_FREIGABEMAIL_NOTE) {
$studlist .= "<td><b>" . $this->p->t('person','personenkennzeichen') . "</b></td>\n
<td><b>" . $this->p->t('lehre','studiengang') . "</b></td>\n
<td><b>" . $this->p->t('benotungstool','c4nachname') . "</b></td>\n
<td><b>" . $this->p->t('benotungstool','c4vorname') . "</b></td>\n";
$studlist .= "<td><b>" . $this->p->t('benotungstool','c4grade') . "</b></td>\n";
$studlist .= "<td><b>" . $this->p->t('ui','bearbeitetVon') . "</b></td></tr>\n";
} else {
$studlist .= "<td><b>" . $this->p->t('person','uid') . "</b></td></tr>\n";
}
foreach($result->noten as $note) {
$resultLVGes = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $note->uid, $sem_kurzbz);
if (!isError($resultLVGes) && hasData($resultLVGes))
{
$lvgesamtnote = getData($resultLVGes)[0];
if ($lvgesamtnote->benotungsdatum > $lvgesamtnote->freigabedatum)
{
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => $note->note,
'freigabevon_uid' => getAuthUID(),
'freigabedatum' => date("Y-m-d H:i:s"),
'updateamum' => date("Y-m-d H:i:s"),
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) {
$lvgesamtnote = getData($res)[0];
$ret[] = array('uid' => $note->uid, 'freigabedatum' => $lvgesamtnote->freigabedatum, 'benotungsdatum' => $lvgesamtnote->benotungsdatum);
}
}
if (defined('CIS_GESAMTNOTE_FREIGABEMAIL_NOTE') && CIS_GESAMTNOTE_FREIGABEMAIL_NOTE)
{
$studlist .= "<tr><td>" . trim($note->matrikelnr) . "</td>";
$studlist .= "<td>" . trim($note->kuerzel) . "</td>";
$studlist .= "<td>" . trim($note->nachname) . "</td>";
$studlist .= "<td>" . trim($note->vorname) . "</td>";
// TODO: if defined(CIS_PUNKTE) ...
$studlist .= "<td>" .$note->noteBezeichnung. "</td>";
$studlist .= "<td>" . $lvgesamtnote->mitarbeiter_uid;
if ($lvgesamtnote->updatevon != '')
$studlist .= " (" . $lvgesamtnote->updatevon . ")";
$studlist .= "</td></tr>";
} else {
$studlist .= "<tr><td>" . trim($note->uid) . "</td></tr>\n";
}
}
}
}
$studlist .= "</table>";
// always send the mail, config toggles data contents
$this->sendEmail($lektorFullName, $lvaFullName, count($result->noten), $emails, $studlist, $betreff);
$this->terminateWithSuccess($ret);
}
private function sendEmail($lektorFullName, $lvaFullName, $notenCount, $emailAdressen, $studlist, $betreff)
{
$emailAdressen[] = getAuthUID() . "@" . DOMAIN; // also send mail to lektors own adress
$adressen = implode(";", $emailAdressen);
foreach ($emailAdressen as $email)
{
// Prepare mail content
$body_fields = array(
'lektor' => $lektorFullName,
'lvaname' => $lvaFullName,
'studlist' => $studlist,
'neuenotencount' => $notenCount,
'adressen' => $adressen
);
// Send mail
sendSanchoMail(
'Notenfreigabe',
$body_fields,
$email,
$betreff
);
}
}
/**
* GET METHOD
* should return Notenvorschlag for single Students, not yet implemented since it is not needed anywhere right now,
* but could be useful later on.
*/
public function getNotenvorschlagStudent() {
// TODO: Notenvorschlag laden allgemeiner Endpunkt, der im Backend mit Logik (z.B. Moodle) angepasst werden kann.
$this->terminateWithSuccess();
}
/**
* POST METHOD
* expects 'datum', 'lva_id', 'student_uid', 'note'
* Inserts or updates a pruefung for lva & student_uid at given datum (YYYY-MM-DD). When creating a new
* Pruefung, sets the provided (Prüfungs-) Note.
* Updates the LvGesamtnote of student.
* Can return 1 or 2 Prüfungen, since the original grade before the first prüfung is being saved as "Termin1" when
* a "Termin2" is being created.
*/
public function saveStudentPruefung() { // einzelne pruefung speichern
$result = $this->getPostJSON();
if(!property_exists($result, 'datum') || !property_exists($result, 'lva_id') ||
!property_exists($result, 'student_uid') || !property_exists($result, 'note')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$student_uid = $result->student_uid;
$note = $result->note;
$punkte = null;
$datum = $result->datum;
$lva_id = $result->lva_id;
$lehreinheit_id = $result->lehreinheit_id;
$stsem = $result->sem_kurzbz;
$typ = $result->typ;
$jetzt = date("Y-m-d H:i:s");
// nachpruefungeintragen.php script calls query on campus.student_lehrveranstaltung to find a
// lehreinheit_id for lva_id -> lehreinheit should be determined prior to that in new benotungstool
// by retrieving it from students row in campus.vw_student_lehrveranstaltung earlier on
// $lehreinheit_id = getLehreinheit($db, $lvid, $student_uid, $stsem);
// $lehreinheit_id = $result->lehreinheit_id;
$punkte = null;
// if($punkte!='')
// {
// // Bei Punkteeingabe wird die Note nochmals geprueft und ggf korrigiert
// $notenschluessel = new notenschluessel();
// $note_pruef = $notenschluessel->getNote($punkte, $lva_id, $stsem);
// if($note_pruef!=$note)
// {
// $note = $note_pruef;
// $note_dirty=true;
// }
// }
// TODO: more sophisticated empty check
if($note=='')
$note = 9;
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$res = $this->LehrveranstaltungModel->load($lva_id);
if(isError($res) || !hasData($res)) {
$this->terminateWithError('Keine gültige Lehrveranstaltung gefunden für ID: '.$lva_id);
}
$studiengang_kz = getData($res)[0]->studiengang_kz;
$res = $this->StudiengangModel->load($studiengang_kz);
if(isError($res) || !hasData($res)) {
$this->terminateWithError('Kein gültiger Studiengang gefunden für ID: '.$studiengang_kz);
}
$pruefungenChanged = $this->savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum);
//Gesamtnote updaten
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lva_id, $student_uid, $stsem);
if(!isError($result) && !hasData($result)) {
$id = $this->LvgesamtnoteModel->insert(
array(
'student_uid' => $student_uid,
'lehrveranstaltung_id' => $lva_id,
'studiensemester_kurzbz' => $stsem,
'note' => $note,
'punkte' => $punkte,
'mitarbeiter_uid' => getAuthUID(),
'benotungsdatum' => $jetzt,
'freigabedatum' => null,
'freigabevon_uid' => null,
'bemerkung' => null,
'updateamum' => null,
'updatevon' => null,
'insertamum' => $jetzt,
'insertvon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
else if(!isError($result) && hasData($result))
{
$lvgesamtnote = getData($result)[0];
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => $note,
'punkte' => $punkte,
'benotungsdatum' => $jetzt,
'updateamum' => $jetzt,
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
$savedPruefung = $pruefungenChanged['savedPruefung'] ?? null;
$extraPruefung = $pruefungenChanged['extraPruefung'] ?? null;
$savedPruefungData = count($savedPruefung) > 0 ? $savedPruefung[0] : null;
$extraPruefungData = count($extraPruefung) > 0 ? $extraPruefung[0] : null;
$this->terminateWithSuccess(array($savedPruefungData, $lvgesamtnote, $extraPruefungData));
}
/**
* private helper method to update/insert pruefungstermine
*/
private function savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum)
{
$status = [];
// send $grades reference to moodle addon
Events::trigger(
'getEntschuldigungsStatusForStudentOnDate',
function & () use (&$status)
{
return $status;
},
[
'student_uid' => $student_uid,
'datum' => $datum
]
);
if(count($status) > 0 && $status[0] == true) {
$note = 17; //entschuldigt
}
$jetzt = date("Y-m-d H:i:s");
$pruefungenChanged = [];
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
if($typ == "Termin2" && defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN2') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN2)
{
// Wenn eine Nachprüfung angelegt wird, wird zuerst eine Pruefung mit 1. Termin angelegt welche für die ursprüngliche Note
// vor den Prüfungsantritten zählt
$result1 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin1", $lva_id, $stsem);
// if there is a termin 1 entry already do nothing
if(!isError($result1) && hasData($result1)) {
} else if(!isError($result1) && !hasData($result1)) {
// new entry termin1
$resultLV = $this->LvgesamtnoteModel->getLvGesamtNoten($lva_id, $student_uid, $stsem);
// update Termin1 note
if (hasData($resultLV))
{
$lvgesamtnote = getData($resultLV)[0];
$pr_note = $lvgesamtnote->note;
$pr_punkte = $lvgesamtnote->punkte;
$benotungsdatum = $lvgesamtnote->benotungsdatum;
}
else if(!hasData($resultLV))// set Termin1 note to "noch nicht eingetragen"
{
$pr_note = 9;
$pr_punkte = null;
$benotungsdatum = $jetzt;
}
$id = $this->LePruefungModel->insert(
array(
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $pr_note,
// 'punkte' => $pr_punkte,
'pruefungstyp_kurzbz' => "Termin1",
'datum' => $benotungsdatum,
'anmerkung' => "",
'insertamum' => $jetzt,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['extraPruefung'] = getData($res);
}
}
// Die Pruefung wird als Termin2 eingetragen
$result2 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin2", $lva_id, $stsem);
// if there is a termin 2 entry already update it
if(!isError($result2) && hasData($result2)) {
// update
$termin2 = getData($result2)[0];
$id = $this->LePruefungModel->update(
$termin2->pruefung_id,
array(
'updateamum' => $jetzt,
'updatevon' => getAuthUID(),
'note' => $note,
// 'punkte' => $punkte,
'datum' => $datum,
'anmerkung' => ""
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
} else if(!isError($result2) && !hasData($result2)) {
// new entry termin 2
$id = $this->LePruefungModel->insert(
array(
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $note,
// 'punkte' => null,//$punkte,
'pruefungstyp_kurzbz' => $typ,
'datum' => $datum,
'anmerkung' => "",
'insertamum' => $jetzt,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
}
} else if($typ == "Termin3" && defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3)
{
$result3 = $this->LePruefungModel->getPruefungenByUidTypLvStudiensemester($student_uid, "Termin3", $lva_id, $stsem);
if(!isError($result3) && hasData($result3)) {
// update
$termin3 = getData($result3)[0];
$id = $this->LePruefungModel->update(
$termin3->pruefung_id,
array(
'updateamum' => $jetzt,
'updatevon' => getAuthUID(),
'note' => $note,
// 'punkte' => $punkte,
'datum' => $datum,
'anmerkung' => ""
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
} else if(!isError($result3) && !hasData($result3)) {
// insert new termin3
$id = $this->LePruefungModel->insert(
array(
'lehreinheit_id' => $lehreinheit_id,
'student_uid' => $student_uid,
'mitarbeiter_uid' => getAuthUID(),
'note' => $note,
// 'punkte' => null,//$punkte,
'pruefungstyp_kurzbz' => $typ,
'datum' => $datum,
'anmerkung' => "",
'insertamum' => $jetzt,
'insertvon' => getAuthUID(),
'updateamum' => null,
'updatevon' => null,
'ext_id' => null
)
);
if($id) {
$res = $this->LePruefungModel->load($id->retval);
if(hasData($res)) $pruefungenChanged['savedPruefung'] = getData($res);
}
}
} else {
$this->terminateWithError($this->p->t('benotungstool', 'wrongPruefungType', [$student_uid, $typ]), 'general');
}
return $pruefungenChanged;
}
/**
* POST METHOD
* expects 'sem_kurzbz', 'lv_id', 'student_uid', 'note'
* Method that sets lv_note of student in lva and semester from provided Points/Grade Selection.
* Updates the note & benotungsdatum, which is key in the noten state offen/freigegeben/changed
*/
public function saveNotenvorschlag() {
$result = $this->getPostJSON();
if(!property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz') ||
!property_exists($result, 'student_uid') || !property_exists($result, 'note')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$lv_id = $result->lv_id;
$student_uid = $result->student_uid;
$sem_kurzbz = $result->sem_kurzbz;
$note = $result->note;
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $student_uid, $sem_kurzbz);
if(!isError($result) && hasData($result)) {
$lvgesamtnote = getData($result)[0];
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => $note,
'punkte' => null,
'benotungsdatum' => date("Y-m-d H:i:s"),
'updateamum' => date("Y-m-d H:i:s"),
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
} else if(!isError($result) && !hasData($result)) {
$id = $this->LvgesamtnoteModel->insert(
array(
'student_uid' => $student_uid,
'lehrveranstaltung_id' => $lv_id,
'studiensemester_kurzbz' => $sem_kurzbz,
'note' => $note,
'punkte' => null,
'mitarbeiter_uid' => getAuthUID(),
'benotungsdatum' => date("Y-m-d H:i:s"),
'freigabedatum' => null,
'freigabevon_uid' => null,
'bemerkung' => null,
'updateamum' => null,
'updatevon' => null,
'insertamum' => date("Y-m-d H:i:s"),
'insertvon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
$this->terminateWithSuccess(array($lvgesamtnote));
}
/**
* POST METHOD
* expects 'sem_kurzbz', 'lv_id', 'noten'
* Bulk variant of saveNotenvorschlag, used when importing grades from csv.
*/
public function saveNotenvorschlagBulk() {
$result = $this->getPostJSON();
if(!property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz') ||
!property_exists($result, 'noten')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$noten = $result->noten;
$retLvNoten = [];
foreach($noten as $note)
{
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $note->uid, $sem_kurzbz);
if(!isError($result) && hasData($result)) {
$lvgesamtnote = getData($result)[0];
$id = $this->LvgesamtnoteModel->update(
[$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id],
array(
'note' => trim($note->note),
'punkte' => null,
'benotungsdatum' => date("Y-m-d H:i:s"),
'updateamum' => date("Y-m-d H:i:s"),
'updatevon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
} else if(!isError($result) && !hasData($result)) {
$id = $this->LvgesamtnoteModel->insert(
array(
'student_uid' => $note->uid,
'lehrveranstaltung_id' => $lv_id,
'studiensemester_kurzbz' => $sem_kurzbz,
'note' => trim($note->note),
'punkte' => null,
'mitarbeiter_uid' => getAuthUID(),
'benotungsdatum' => date("Y-m-d H:i:s"),
'freigabedatum' => null,
'freigabevon_uid' => null,
'bemerkung' => null,
'updateamum' => null,
'updatevon' => null,
'insertamum' => date("Y-m-d H:i:s"),
'insertvon' => getAuthUID()
)
);
if($id) {
$res = $this->LvgesamtnoteModel->load($id->retval);
if(hasData($res)) $lvgesamtnote = getData($res)[0];
}
}
$retLvNoten[] = $lvgesamtnote;
}
$this->terminateWithSuccess($retLvNoten);
}
/**
* POST METHOD
* expects 'uids', 'datum'
* Bulk variant of saveStudentPruefung, used when creating a new Prüfung for several students. Always sets note to
* "noch nicht eingetragen" for the created Prüfung.
*/
public function createPruefungen() {
$result = $this->getPostJSON();
if(!property_exists($result, 'uids') || !property_exists($result, 'datum')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$uids = $result->uids;
$datum = $result->datum;
$lva_id = $result->lva_id;
$stsem = $result->sem_kurzbz;
$ret = [];
foreach ($uids as $student) {
$student_uid = $student->uid;
$typ = $student->typ;
$note = 9; //$result->note; // TODO: parameterize for import maybe
$punkte = ''; // TODO: check punkte feature
$lehreinheit_id = $student->lehreinheit_id;
$ret[$student->uid] = $this->savePruefungstermin($typ, $student_uid, $lva_id, $stsem, $lehreinheit_id, $note, $punkte, $datum);
}
$this->terminateWithSuccess($ret);
}
/**
* POST METHOD
* expects 'lv_id', 'sem_kurzbz', 'pruefungen'
* Bulk variant of saveStudentPruefung, used when importing pruefungsdata from csv with available noten.
*/
public function savePruefungenBulk() {
$result = $this->getPostJSON();
if(!property_exists($result, 'lv_id') || !property_exists($result, 'sem_kurzbz') ||
!property_exists($result, 'pruefungen')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$pruefungen = $result->pruefungen;
$ret = [];
foreach ($pruefungen as $pruefung) {
$student_uid = $pruefung->uid;
$typ = $pruefung->typ;
$note = $pruefung->note; // TODO: parameterize for import maybe
$datum = $pruefung->datum;
$punkte = ''; // TODO: check punkte feature
$lehreinheit_id = $pruefung->lehreinheit_id;
$ret[$student_uid] = $this->savePruefungstermin($typ, $student_uid, $lv_id, $sem_kurzbz, $lehreinheit_id, $note, $punkte, $datum);
}
$this->terminateWithSuccess($ret);
}
}
@@ -1,67 +0,0 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Studiensemester extends FHCAPI_Controller
{
private $_ci;
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getStudiensemester'=> self::PERM_LOGGED,
]);
$this->_ci =& get_instance();
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* GET METHOD
* returns List of all studiensemester as well as current one
*/
public function getStudiensemester()
{
$this->_ci->StudiensemesterModel->addOrder("start", "DESC");
$result = $this->_ci->StudiensemesterModel->load();
$studiensemester = getData($result);
$result = $this->_ci->StudiensemesterModel->getAkt();
$aktuell = getData($result);
$this->terminateWithSuccess(array($studiensemester, $aktuell));
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -1,86 +0,0 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Config extends FHCAPI_Controller
{
private $_ci;
private $_uid;
public function __construct()
{
parent::__construct([
'get' => ['admin:r', 'assistenz:r'],
'set' => ['admin:r', 'assistenz:r'],
]);
$this->_ci = &get_instance();
$this->_setAuthUID();
$this->loadPhrases([
'lehre'
]);
$this->_ci->load->library('VariableLib', ['uid' => $this->_uid]);
$this->_ci->load->library('PermissionLib');
}
public function get()
{
if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan')))
$this->terminateWithSuccess([]);
$ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision');
$ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre');
$ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung');
$config['ignore_kollision'] = [
"type" => "checkbox",
"label" => 'ignore_kollision',
"value" => $ignore_kollision,
];
$config['ignore_zeitsperre'] = [
"type" => "checkbox",
"label" => 'ignore_zeitsperre',
"value" => $ignore_zeitsperre,
];
$config['ignore_reservierung'] = [
"type" => "checkbox",
"label" => 'ignore_reservierung',
"value" => $ignore_reservierung,
];
$this->terminateWithSuccess($config);
}
public function set()
{
if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan')))
$this->terminateWithSuccess([]);
$this->load->model('system/Variable_model', 'VariableModel');
foreach (['ignore_kollision','ignore_zeitsperre','ignore_reservierung'] as $variable)
{
if ($this->_ci->input->post($variable) !== null)
{
$this->VariableModel->update(array('uid' => $this->_uid, 'name' => $variable), array('wert' => $this->input->post($variable)));
}
}
$this->terminateWithSuccess();
}
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid)
show_error('User authentification failed');
}
}
@@ -15,7 +15,6 @@ class Gruppe extends FHCAPI_Controller
'getBenutzerSearch' => ['admin:r', 'assistenz:r'],
'getAllSearch' => ['admin:r', 'assistenz:r'],
'getByLehreinheit' => ['admin:r', 'assistenz:r'],
'getGruppe' => ['admin:r', 'assistenz:r'],
]);
$this->_ci = &get_instance();
@@ -68,7 +67,7 @@ class Gruppe extends FHCAPI_Controller
$this->checkPermission($lehreinheit_id);
$result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, $lehrverband === true || $lehrverband === 'true');
$result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, !($lehrverband === 'false'));
if (isError($result))
$this->terminateWithError(getError($result));
@@ -76,71 +75,6 @@ class Gruppe extends FHCAPI_Controller
$this->terminateWithSuccess($result);
}
public function getGruppe()
{
$lehrverband = $this->input->post('lehrverband');
$gruppen_result = array();
if ($lehrverband === false)
{
$gruppen_result = $this->_ci->GruppeModel->loadWhere(array(
'studiengang_kz' => $this->input->post('stg_kz'),
'gruppe_kurzbz' => $this->input->post('gruppe_kurzbz'),
'aktiv' => true
));
}
else if ($lehrverband === true)
{
if (!isEmptyString($this->input->post('verband')))
{
$this->LehrverbandModel->db->where('verband', $this->input->post('verband'));
}
else
{
$this->LehrverbandModel->db->group_start();
$this->LehrverbandModel->db->where("trim(verband) = ''");
$this->LehrverbandModel->db->or_where("verband IS NULL");
$this->LehrverbandModel->db->group_end();
}
if (!isEmptyString($this->input->post('gruppe')))
{
$this->LehrverbandModel->db->where('gruppe', $this->input->post('gruppe'));
}
else
{
$this->LehrverbandModel->db->group_start();
$this->LehrverbandModel->db->where("trim(gruppe) = ''");
$this->LehrverbandModel->db->or_where("gruppe IS NULL");
$this->LehrverbandModel->db->group_end();
}
if (!isEmptyString((string)$this->input->post('semester')))
{
$this->LehrverbandModel->db->where('semester', $this->input->post('semester'));
}
else
{
$this->LehrverbandModel->db->group_start();
$this->LehrverbandModel->db->where("semester = ''");
$this->LehrverbandModel->db->or_where("semester IS NULL");
$this->LehrverbandModel->db->group_end();
}
$gruppen_result = $this->LehrverbandModel->loadWhere(array('studiengang_kz' => $this->input->post('stg_kz'), 'aktiv' => true));
}
if (!hasData($gruppen_result))
return $this->terminateWithError('No group found');
$gruppen_array = getData($gruppen_result)[0];
$this->terminateWithSuccess($gruppen_array->gid);
}
public function getByLehreinheit($lehreinheit_id = null)
{
if (is_null($lehreinheit_id) || !ctype_digit((string)$lehreinheit_id))
@@ -134,14 +134,9 @@ class Lektor extends FHCAPI_Controller
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$nullable_fields = array('semesterstunden', 'stundensatz', 'planstunden');
foreach ($nullable_fields as $nullable_field)
if (isset($formData['semesterstunden']) && (!is_numeric($formData['semesterstunden']) || $formData['semesterstunden'] === ''))
{
if (isset($formData[$nullable_field]) && (!is_numeric($formData[$nullable_field]) || $formData[$nullable_field] === ''))
{
$formData[$nullable_field] = null;
}
$formData['semesterstunden'] = null;
}
$lehreinheit_permission = $this->checkPermission($lehreinheit_id, array('admin', 'assistenz', 'lv-plan'));
@@ -149,9 +144,6 @@ class Lektor extends FHCAPI_Controller
if (!$lehreinheit_permission)
$this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess'));
if (!is_null($this->getLektorVertrag($lehreinheit_id, $mitarbeiter_uid)) && (array_key_exists('mitarbeiter_uid', $formData) && $mitarbeiter_uid !== $formData['mitarbeiter_uid']))
$this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess'));
$result = $this->_ci->lektorlib->updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $formData);
if (isError($result)) $this->terminateWithError(getError($result));
@@ -162,7 +154,7 @@ class Lektor extends FHCAPI_Controller
{
$value = str_replace(',', '.', $value);
if (!is_numeric($value) && $value !== "")
if (!is_numeric($value))
{
$this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss eine Zahl sein.');
return false;
@@ -1,84 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Noten extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getCertificate' => 'student/noten:r',
'getTeacherProposal' => 'student/noten:r',
]);
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
// Load Phrases
$this->loadPhrases([
'stv',
'person',
'lehre'
]);
}
public function getCertificate($lv_id, $studiensemester_kurzbz = null)
{
if (is_null($lv_id) || !ctype_digit((string)$lv_id))
$this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$result = $this->LehrveranstaltungModel->loadWhere([
'lehrveranstaltung_id' => $lv_id
]);
$lehrveranstaltung = $this->getDataOrTerminateWithError($result);
if (!$lehrveranstaltung)
$this->terminateWithSuccess([]);
if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
{
$this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester'));
}
$result = $this->ZeugnisnoteModel->getZeugnisnoten(null, $studiensemester_kurzbz, $lehrveranstaltung[0]->lehrveranstaltung_id);
$grades = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($grades);
}
public function getTeacherProposal($lv_id, $studiensemester_kurzbz = null)
{
if (is_null($lv_id) || !ctype_digit((string)$lv_id))
$this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$result = $this->LehrveranstaltungModel->loadWhere([
'lehrveranstaltung_id' => $lv_id
]);
$lehrveranstaltung = $this->getDataOrTerminateWithError($result);
if (!$lehrveranstaltung)
$this->terminateWithSuccess([]);
if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
{
$this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester'));
}
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, null, $studiensemester_kurzbz);
$grades = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($grades);
}
}
@@ -27,8 +27,7 @@ class Setup extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getLETabs' => ['admin:r', 'assistenz:r'],
'getLVTabs' => ['admin:r', 'assistenz:r'],
'getTabs' => ['admin:r', 'assistenz:r'],
'getStudiensemester' => ['admin:r', 'assistenz:r'],
'getSprache' => ['admin:r', 'assistenz:r'],
'getRaumtyp' => ['admin:r', 'assistenz:r'],
@@ -42,10 +41,9 @@ class Setup extends FHCAPI_Controller
$this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->_ci->load->library('VariableLib', ['uid' => $this->_uid]);
$this->_ci->load->helper('hlp_document');
}
public function getLETabs()
public function getTabs()
{
$tabs['details'] = array (
'title' => 'Details',
@@ -62,11 +60,6 @@ class Setup extends FHCAPI_Controller
'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Lektor.js'),
'config' => []
);
$tabs['termine'] = array (
'title' => 'Termine',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Termine.js',
'config' => []
);
$tabs['notiz'] = array (
'title' => 'Notizen',
'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Notiz.js'),
@@ -75,28 +68,6 @@ class Setup extends FHCAPI_Controller
$this->terminateWithSuccess($tabs);
}
public function getLVTabs()
{
$tabs['termine'] = array (
'title' => 'Termine',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/LVTermine.js',
'config' => []
);
$tabs['noten'] = array (
'title' => 'Noten',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Noten.js',
'config' => [
'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE,
'edit' => 'both', // Possible values: both|header|inline
'delete' => 'inline', // Possible values: both|header|inline
'documents' => 'inline', // Possible values: both|header|inline
'documentslist' => gradesDocumentsList(),
'semesterSelect' => false
]
);
$this->terminateWithSuccess($tabs);
}
public function getStudiensemester()
{
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
@@ -25,25 +25,7 @@ class StgTree extends FHCAPI_Controller
return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]);
}
$count = count($params);
if (!$count)
return $this->getStudiengang($method);
if ($count == 1) {
if (is_numeric($params[0]))
return $this->getSemester($method, $params[0]);
else
return $this->getStudiengang($method, $params[0]);
}
if ($count == 2) {
if (is_numeric($params[0]))
return $this->getVerband($method, $params[0], $params[1]);
else
return $this->getSemester($method, $params[1], $params[0]);
}
return $this->getStudiengang($method);
show_404();
}
@@ -82,27 +64,16 @@ class StgTree extends FHCAPI_Controller
$this->terminateWithSuccess($list);
}
protected function getStudiengang($studiengang_kz, $org_form = null)
protected function getStudiengang($studiengang_kz)
{
$link = $studiengang_kz . '/';
if ($org_form !== null)
$link .= $org_form . '/';
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
$this->StudiengangModel->addDistinct();
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false);
$this->StudiengangModel->addSelect("CONCAT(
UPPER(CONCAT(typ, kurzbz)),
'-',
semester,
(
SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END
FROM public.tbl_lehrverband
WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester
ORDER BY verband, gruppe LIMIT 1
)
) AS name", false);
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester ORDER BY verband, gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
$this->StudiengangModel->addSelect('semester');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
@@ -140,115 +111,6 @@ class StgTree extends FHCAPI_Controller
$list = array_merge($list, $result);
}
}
$this->terminateWithSuccess($list);
}
protected function getSemester($studiengang_kz, $semester, $org_form = null)
{
$link = $studiengang_kz . '/';
if ($org_form !== null)
$link .= $org_form . '/';
$link .= $semester . '/';
$this->load->model('organisation/Gruppe_model', 'GruppeModel');
$this->GruppeModel->addDistinct();
$this->GruppeModel->addSelect("CONCAT(" . $this->GruppeModel->escape($link . 'grp/') . ", gruppe_kurzbz) AS link", false);
$this->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false);
$this->GruppeModel->addSelect("TRUE AS leaf", false);
$this->GruppeModel->addSelect('sort');
$this->GruppeModel->addSelect('gruppe_kurzbz');
$this->GruppeModel->addSelect($this->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
$this->GruppeModel->addOrder('sort');
$this->GruppeModel->addOrder('gruppe_kurzbz');
$where = [
'studiengang_kz' => $studiengang_kz,
'semester' => $semester,
'lehre' => true,
'sichtbar' => true,
'aktiv' => true,
'direktinskription' => false
];
if ($org_form !== null)
$where['orgform_kurzbz'] = $org_form;
$result = $this->GruppeModel->loadWhere($where);
$list = $this->getDataOrTerminateWithError($result);
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", verband) AS link", false);
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf");
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester');
$this->StudiengangModel->addSelect('verband');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
$this->StudiengangModel->addOrder('verband');
$this->StudiengangModel->addGroupBy('link, name, verband');
$where = [
'v.studiengang_kz' => $studiengang_kz,
'v.semester' => $semester,
'v.verband !=' => '',
'v.aktiv' => true
];
if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all?
$where['v.orgform_kurzbz'] = $org_form;
$result = $this->StudiengangModel->loadWhere($where);
$result = $this->getDataOrTerminateWithError($result);
$list = array_merge($list, $result);
$this->terminateWithSuccess($list);
}
protected function getVerband($studiengang_kz, $semester, $verband, $org_form = null)
{
$link = $studiengang_kz . '/';
if ($org_form !== null)
$link .= $org_form . '/';
$link .= $semester . '/'. $verband . '/';
$this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz');
$this->StudiengangModel->addDistinct();
$this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", gruppe) AS link", false);
$this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false);
$this->StudiengangModel->addSelect("TRUE AS leaf", false);
$this->StudiengangModel->addSelect('v.semester');
$this->StudiengangModel->addSelect('v.verband');
$this->StudiengangModel->addSelect('gruppe');
$this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false);
$this->StudiengangModel->addOrder('gruppe');
$where = [
'v.studiengang_kz' => $studiengang_kz,
'v.semester' => $semester,
'v.verband' => $verband,
'v.gruppe !=' => '',
'v.aktiv' => true
];
if ($org_form !== null && $semester)
$where['v.orgform_kurzbz'] = $org_form;
$result = $this->StudiengangModel->loadWhere($where);
$list = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($list);
}
@@ -42,14 +42,22 @@ class Messages extends FHCAPI_Controller
]);
}
public function getMessages($id, $type_id, $size, $page)
public function getMessages($id, $type_id, $size=null, $page=null)
{
if($type_id != 'person_id'){
$id = $this->_getPersonId($id, $type_id);
}
$offset = $size * ($page - 1);
$limit = $size;
if(!(is_null($size) && is_null($page)))
{
$offset = $size * ($page - 1);
$limit = $size;
}
else
{
$offset = null;
$limit = null;
}
$result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
@@ -301,7 +301,6 @@ class Config extends FHCAPI_Controller
public function student()
{
$this->load->helper('hlp_document');
$result = [];
$config = $this->config->item('tabs');
@@ -377,9 +376,9 @@ class Config extends FHCAPI_Controller
'config' => [
'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE,
'edit' => 'both', // Possible values: both|header|inline
'delete' => 'inline', // Possible values: both|header|inline
'documents' => 'inline', // Possible values: both|header|inline
'documentslist' => gradesDocumentsList()
'delete' => 'both', // Possible values: both|header|inline
'documents' => 'both', // Possible values: both|header|inline
'documentslist' => $this->gradesDocumentsList()
]
];
@@ -623,6 +622,188 @@ class Config extends FHCAPI_Controller
] + $this->kontoColumns();
}
/**
* Helper function to generate the default documentslist config for the
* grades tab.
*
* The resulting array consists of elements which are associative arrays
* that can have the following entries:
* title (required) on the first level this can be HTML code.
* permissioncheck (optional) an URL to an FHCAPI endpoint which returns
* true or false.
* link (optional) an URL that will be called if "action" and
* "children" are not defined.
* action (optional) an associative array that describes an
* POST action that will be called if "children" is
* not defined.
* It can have the following entries:
* - url (required) an URL to an FHCAPI endpoint.
* - post (optional) an associative array with the POST data to
* be sent.
* - response (optional) a string that will be displayed on success.
* children (optional) an array of child elements
*
* All strings that start with { and end with } in the URLs and the
* actions post parameter will be replaced with the corresponding
* attribute of the current dataset (e.G: {uid} will be replaced with the
* uid of the current dataset)
*
* @return array
*/
protected function gradesDocumentsList()
{
$permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}");
$title_ger = $this->p->t("global", "deutsch");
$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" .
"?stg_kz={studiengang_kz_lv}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_ger = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnis" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_eng = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnisEng" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$archive_url = "api/frontend/v1/documents/archiveSigned";
$archive_response = $this->p->t("stv", "document_signed_and_archived");
$archive_post_ff = [
"xml" => "zertifikat.rdf.php",
"xsl" => "Zertifikat",
"stg_kz" => "{studiengang_kz_lv}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_ger = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnis",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_eng = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnisEng",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$list = [
[
'title' => '<i class="fa fa-download" title="' . $this->p->t("stv", "document_download") . '"></i>',
'children' => [
[
'title' => $title_ff,
'link' => site_url($link_ff)
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'link' => site_url($link_lv_ger),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=odt")
]
]
],
[
'title' => $title_eng,
'link' => site_url($link_lv_eng),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=odt")
]
]
]
]
]
]
],
[
'title' => '<i class="fas fa-archive" title="' . $this->p->t("stv", "document_archive") . '"></i>',
'children' => [
[
'title' => $title_ff,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_ff,
'response' => $archive_response
]
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_ger,
'response' => $archive_response
]
],
[
'title' => $title_eng,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_eng,
'response' => $archive_response
]
]
]
]
]
]
];
return $list;
}
/**
* Sort tab list
*
@@ -78,52 +78,32 @@ class Dokumente extends FHCAPI_Controller
$this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiengang_kz']), self::ERROR_TYPE_GENERAL);
$resultPreDoc = $this->_getPrestudentDokumente($prestudent_id);
$arrayAccepted = [];
$person_id = $this->_getPersonId($prestudent_id);
$docNames = array_map(function ($item) {
return $item->dokument_kurzbz;
}, $resultPreDoc);
$mergedArray = [];
foreach($docNames as $doc)
foreach ($resultPreDoc as $pre)
{
$result = $this->AkteModel->getAktenFAS($person_id, $doc, $studiengang_kz, $prestudent_id, true);
$result = $this->AkteModel->getAktenFAS($person_id, $pre->dokument_kurzbz, $studiengang_kz, $prestudent_id, true);
if (isError($result))
{
return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
}
if (hasData($result))
{
$data = getData($result);
foreach ($data as $value)
foreach (getData($result) as $doc)
{
array_push($arrayAccepted, $value);
$merged = clone $doc;
$merged->docdatum = $pre->docdatum;
$merged->insertvonma = $pre->insertvonma;
$merged->bezeichnung = $pre->bezeichnung;
$mergedArray[] = $merged;
}
}
}
//Mapping with document_kurzbz
$preDocMap = [];
foreach ($resultPreDoc as $pre) {
$preDocMap[$pre->dokument_kurzbz] = $pre;
}
$mergedArray = [];
foreach ($arrayAccepted as $doc) {
$merged = clone $doc;
if (isset($preDocMap[$doc->dokument_kurzbz])) {
$merged->docdatum = $preDocMap[$doc->dokument_kurzbz]->docdatum;
$merged->insertvonma = $preDocMap[$doc->dokument_kurzbz]->insertvonma;
$merged->bezeichnung = $preDocMap[$doc->dokument_kurzbz]->bezeichnung;
} else {
$merged->akzeptiertdatum = null;
$merged->akzeptiertvon = null;
else
{
$mergedArray[] = $pre;
}
$mergedArray[] = $merged;
}
$this->terminateWithSuccess($mergedArray);
@@ -48,7 +48,8 @@ class Konto extends FHCAPI_Controller
// Load language phrases
$this->loadPhrases([
'konto'
'konto',
'lehre'
]);
}
@@ -112,7 +113,7 @@ class Konto extends FHCAPI_Controller
*
* @return void
*/
public function getBuchungstypen()
public function getBuchungstypen($studiensemester_kurzbz = null)
{
$this->load->model('crm/Buchungstyp_model', 'BuchungstypModel');
@@ -122,6 +123,7 @@ class Konto extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->_getOEHBeitrag($data, $studiensemester_kurzbz);
$this->terminateWithSuccess($data);
}
@@ -494,4 +496,43 @@ class Konto extends FHCAPI_Controller
$this->terminateWithSuccess();
}
private function _getOEHBeitrag(&$data, $studiensemester_kurzbz = null)
{
if (is_null($studiensemester_kurzbz))
{
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
$studiensemester_akt = $this->variablelib->getVar('semester_aktuell');
}
else
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
if ($this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz))
$studiensemester_akt = $studiensemester_kurzbz;
else
$this->terminateWithError($this->p->t('lehre', 'error_noStudiensemester'));
}
$this->load->model('codex/Oehbeitrag_model', 'OehbeitragModel');
$oehBeitrag = $this->OehbeitragModel->getByStudiensemester($studiensemester_akt);
$oehStandardbetrag = null;
if (hasData($oehBeitrag))
{
$oeh = getData($oehBeitrag)[0];
$summe = ($oeh->studierendenbeitrag + $oeh->versicherung) * -1;
$oehStandardbetrag = number_format((float)$summe, 2, '.', '');
}
if ($oehStandardbetrag !== null)
{
$data = array_map(function ($buchungstyp) use ($oehStandardbetrag) {
if (isset($buchungstyp->buchungstyp_kurzbz) && (strtolower($buchungstyp->buchungstyp_kurzbz) === 'oeh'))
{
$buchungstyp->standardbetrag = $oehStandardbetrag;
}
return $buchungstyp;
}, $data);
}
}
}
@@ -34,7 +34,7 @@ class LvTermine extends FHCAPI_Controller
//TODO Build own lib or combine with Controller Stundenplan.php
//here use of logic of Stundenplan.php, extended with parameters uid, grouping, and used dbTable
public function getStundenplan($uid, $start_date = null, $end_date = null, $dbStundenplanTable = "stundenplan", $groupConsecutiveHours = false)
public function getStundenplan($uid, $start_date = null, $end_date = null, $groupConsecutiveHours = false, $dbStundenplanTable = "stundenplan")
{
$student_uid = $uid;
$semester_range = $this->studienSemesterErmitteln($start_date, $end_date);
@@ -80,6 +80,13 @@ class LvTermine extends FHCAPI_Controller
$stundenplan_data = $this->getDataOrTerminateWithError($stundenplan_data) ?? [];
$this->terminateWithSuccess($stundenplan_data);
$this->expand_object_information($stundenplan_data);
$this->returnObj['$stundenplan_query'] = $stundenplan_query;
$this->returnObj['$student_lehrverband'] = $student_lehrverband;
$this->returnObj['$benutzer_gruppen'] = $benutzer_gruppen;
$this->terminateWithSuccess($stundenplan_data);
}
public function getStudiensemester()
@@ -90,6 +90,15 @@ class Projektarbeit extends FHCAPI_Controller
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id)) return $this->terminateWithError('Projektarbeit Id missing', self::ERROR_TYPE_GENERAL);
$result = $this->fetchProjektarbeitByID($projektarbeit_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
private function fetchProjektarbeitById($projektarbeit_id) {
$this->ProjektarbeitModel->resetQuery();
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
@@ -97,13 +106,10 @@ 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', 'LEFT');
$result = $this->ProjektarbeitModel->loadWhere(
return $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
/**
@@ -132,7 +138,8 @@ class Projektarbeit extends FHCAPI_Controller
);
$data = $this->getDataOrTerminateWithError($result);
$data = $this->getDataOrTerminateWithError($this->fetchProjektarbeitById($data));
$this->terminateWithSuccess($data);
}
@@ -358,7 +358,8 @@ class AbgabetoolJob extends JOB_Controller
foreach($assistenzMap as $assistenz_person_id => $tupelArr) {
$abgabenString = '<div style="font-family: Arial, sans-serif; color: #333;">';
$hasContent = false;
foreach($tupelArr as $tupel) {
$projektarbeit_id = $tupel[0];
$assistenzRow = $tupel[1];
@@ -377,6 +378,7 @@ class AbgabetoolJob extends JOB_Controller
if(count($relevantAbgaben) == 0) {
continue;
}
$hasContent = true;
// Format the Student Name
$s = $relevantAbgaben[0];
@@ -447,7 +449,12 @@ class AbgabetoolJob extends JOB_Controller
}
$abgabenString .= '</div>';
// skip send entirely
if (!$hasContent) {
continue;
}
// done with building the change list, now send it
$assistenzRow = $tupelArr[0][1];
$anrede = $assistenzRow->anrede;
+1
View File
@@ -417,6 +417,7 @@ abstract class Notiz_Controller extends FHCAPI_Controller
$notiz_id = $this->input->post('notiz_id');
$this->NotizModel->addSelect('campus.tbl_dms_version.*');
$this->NotizModel->addSelect($this->NotizModel->escape(base_url('content/notizdokdownload.php?id=')) . ' || public.tbl_notiz_dokument.dms_id AS preview');
$this->NotizModel->addJoin('public.tbl_notiz_dokument', 'ON (public.tbl_notiz_dokument.notiz_id = public.tbl_notiz.notiz_id)');
$this->NotizModel->addJoin('campus.tbl_dms_version', 'ON (public.tbl_notiz_dokument.dms_id = campus.tbl_dms_version.dms_id)');
-188
View File
@@ -1,188 +0,0 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Helper function to generate the default documentslist config for the
* grades tab.
*
* The resulting array consists of elements which are associative arrays
* that can have the following entries:
* title (required) on the first level this can be HTML code.
* permissioncheck (optional) an URL to an FHCAPI endpoint which returns
* true or false.
* link (optional) an URL that will be called if "action" and
* "children" are not defined.
* action (optional) an associative array that describes an
* POST action that will be called if "children" is
* not defined.
* It can have the following entries:
* - url (required) an URL to an FHCAPI endpoint.
* - post (optional) an associative array with the POST data to
* be sent.
* - response (optional) a string that will be displayed on success.
* children (optional) an array of child elements
*
* All strings that start with { and end with } in the URLs and the
* actions post parameter will be replaced with the corresponding
* attribute of the current dataset (e.G: {uid} will be replaced with the
* uid of the current dataset)
*
* @return array
*/
function gradesDocumentsList()
{
$ci =& get_instance();
$ci->load->library('PhrasesLib', array('stv'), 'p');
$permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}");
$title_ger = $ci->p->t("global", "deutsch");
$title_eng = $ci->p->t("global", "englisch");
$title_ff = $ci->p->t("stv", "document_certificate");
$title_lv = $ci->p->t("stv", "document_coursecertificate");
$link_ff = "documents/export/" .
"zertifikat.rdf.php/" .
"Zertifikat" .
"?stg_kz={studiengang_kz_lv}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_ger = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnis" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$link_lv_eng = "documents/export/" .
"lehrveranstaltungszeugnis.rdf.php/" .
"LVZeugnisEng" .
"?stg_kz={studiengang_kz}" .
"&uid={uid}" .
"&ss={studiensemester_kurzbz}" .
"&lvid={lehrveranstaltung_id}";
$archive_url = "api/frontend/v1/documents/archiveSigned";
$archive_response = $ci->p->t("stv", "document_signed_and_archived");
$archive_post_ff = [
"xml" => "zertifikat.rdf.php",
"xsl" => "Zertifikat",
"stg_kz" => "{studiengang_kz_lv}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_ger = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnis",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$archive_post_lv_eng = [
"xml" => "lehrveranstaltungszeugnis.rdf.php",
"xsl" => "LVZeugnisEng",
"stg_kz" => "{studiengang_kz}",
"uid" => "{uid}",
"ss" => "{studiensemester_kurzbz}",
"lvid" => "{lehrveranstaltung_id}"
];
$list = [
[
'title' => '<i class="fa fa-download" title="' . $ci->p->t("stv", "document_download") . '"></i>',
'children' => [
[
'title' => $title_ff,
'link' => site_url($link_ff)
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'link' => site_url($link_lv_ger),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_ger . "&output=odt")
]
]
],
[
'title' => $title_eng,
'link' => site_url($link_lv_eng),
'children' => [
[
'title' => 'PDF',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng)
],
[
'title' => 'DOC',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=doc")
],
[
'title' => 'ODT',
'permissioncheck' => $permissioncheck,
'link' => site_url($link_lv_eng . "&output=odt")
]
]
]
]
]
]
],
[
'title' => '<i class="fas fa-archive" title="' . $ci->p->t("stv", "document_archive") . '"></i>',
'children' => [
[
'title' => $title_ff,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_ff,
'response' => $archive_response
]
],
[
'title' => $title_lv,
'children' => [
[
'title' => $title_ger,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_ger,
'response' => $archive_response
]
],
[
'title' => $title_eng,
'action' => [
'url' => site_url($archive_url),
'post' => $archive_post_lv_eng,
'response' => $archive_response
]
]
]
]
]
]
];
return $list;
}
+1 -1
View File
@@ -128,7 +128,7 @@ class AntragLib
return $this->_ci->StudierendenantragstatusModel->resumeAntraegeForAbmeldungStgl($antrag_id);
}
// NOTE(chris): get last status that is not pause
$this->_ci->StudierendenantragstatusModel->addOrder('insertamum');
$this->_ci->StudierendenantragstatusModel->addOrder('insertamum', 'DESC');
$this->_ci->StudierendenantragstatusModel->addLimit(1);
$result = $this->_ci->StudierendenantragstatusModel->loadWhere([
'studierendenantrag_id' => $antrag_id,
+79 -175
View File
@@ -17,12 +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->model('ressource/Zeitsperre_model', 'ZeitsperreModel');
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
$this->_ci->load->library('PhrasesLib', array('lehre'));
$this->_ci->load->library('VariableLib', ['uid' => getAuthUID()]);
}
public function addLektorToLehreinheit($lehreinheit_id, $mitarbeiter_uid)
@@ -83,13 +78,10 @@ class LektorLib
public function updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $new_data)
{
$old_uid = $mitarbeiter_uid;
$new_uid = isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid;
$this->_ci->LehreinheitmitarbeiterModel->addSelect('lehre.tbl_lehreinheitmitarbeiter.*, lehre.tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.studiengang_kz');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id');
$lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid));
$lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid));
if (isError($lehreinheit_result)) return $lehreinheit_result;
@@ -97,47 +89,27 @@ class LektorLib
$lehreinheit = getData($lehreinheit_result)[0];
$semesterstunden_alt = $lehreinheit->semesterstunden;
$semesterstunden_neu = isset($new_data['semesterstunden']) ? $new_data['semesterstunden'] : $semesterstunden_alt;
$bismelden_neu = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden;
$neue_stunden_eingerechnet = (bool)$bismelden_neu;
$alte_stunden_eingerechnet = (bool)$lehreinheit->bismelden;
$stundenplan_update = false;
if ($old_uid !== $new_uid)
//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)
{
$lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_uid, 'lehreinheit_id' => $lehreinheit_id));
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
$this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id');
$this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid');
$this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid');
$verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid));
if (hasData($verplant))
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"));
$this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id');
$this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid');
$verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid));
if (hasData($verplant))
{
$kollision = $this->hasKollision(getData($verplant), $new_uid);
$ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision');
if ($kollision === false || $ignore_kollision == 'true')
{
$stundenplan_update = true;
}
else if (is_array($kollision))
{
return error( "Änderung fehlgeschlagen! Die Änderung des Lektors führt zu ".count($kollision)." Kollision(en) im LV-Plan. Deaktivieren Sie die Kollisionspruefung oder wenden Sie sich an die LV-Planung!\n zB. $kollision[0]");
}
else
{
return error($kollision);
}
}
}
$warning = '';
if (($semesterstunden_neu !== '' && $semesterstunden_alt !== '') && (($semesterstunden_neu > $semesterstunden_alt) || $neue_stunden_eingerechnet))
if (isset($new_data['semesterstunden']))
{
$studiengang_result = $this->_ci->StudiengangModel->loadWhere(array('studiengang_kz' => $lehreinheit->studiengang_kz));
if (isError($studiengang_result)) return $studiengang_result;
@@ -148,7 +120,7 @@ class LektorLib
if (isError($studiensemester_result)) return $studiensemester_result;
$studiensemester = getData($studiensemester_result)[0];
$echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($new_uid, $studiensemester->start, $studiensemester->ende, 'echterdv');
$echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'echterdv');
$echter_dv = false;
@@ -157,78 +129,83 @@ class LektorLib
$echter_dv = true;
}
$stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv);
if (isError($stundengrenze_result)) return $stundengrenze_result;
$neue_stunden_eingerechnet = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden;
$alte_stunden_eingerechnet = $lehreinheit->bismelden;
$stundengrenze = getData($stundengrenze_result)[0];
$oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz);
$oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array();
if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu)}) as summe");
else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt)) as summe");
else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu)})) as summe");
else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe");
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', $new_uid);
$this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz);
$this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true);
$this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%');
if (count($oe_array) > 0)
if (($new_data['semesterstunden'] > $lehreinheit->semesterstunden) || $neue_stunden_eingerechnet)
{
$stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv);
if (isError($stundengrenze_result)) return $stundengrenze_result;
$stundengrenze = getData($stundengrenze_result)[0];
$oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz);
$oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array('');
if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])}) as summe");
else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden)) as summe");
else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])})) as summe");
else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet)
$this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe");
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id');
$this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', (isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid));
$this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz);
$this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true);
$this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%');
$this->_ci->LehreinheitmitarbeiterModel->db->where_in('tbl_studiengang.oe_kurzbz', $oe_array);
}
if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE')
&& is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE)
&& count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0)
{
$this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE);
}
$summe_result = $this->_ci->LehreinheitmitarbeiterModel->load();
if (isError($summe_result)) return $summe_result;
if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden');
$summe = getData($summe_result)[0]->summe;
if ($summe > $stundengrenze->stunden)
{
if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin')))
if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE')
&& is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE)
&& count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0)
{
if (!$this->LehrauftragAufFirma($new_uid))
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!\nDaten wurden gespeichert!\n\n";
$this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE);
}
$stunden_limit_result = $this->getStundenInstitut($new_uid, $lehreinheit->studiensemester_kurzbz, $oe_array);
$summe_result = $this->_ci->LehreinheitmitarbeiterModel->load();
if (hasData($stunden_limit_result))
if (isError($summe_result)) return $summe_result;
if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden');
$summe = getData($summe_result)[0]->summe;
if ($summe > $stundengrenze->stunden)
{
$stunden_limit_array = getData($stunden_limit_result);
foreach ($stunden_limit_array as $stunden_limit)
if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin')))
{
$warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n";
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!\nDaten wurden NICHT gespeichert!\n\n");
}
else
{
$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);
if (hasData($stunden_limit_result))
{
$stunden_limit_array = getData($stunden_limit_result);
foreach ($stunden_limit_array as $stunden_limit)
{
$warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n";
}
}
}
}
}
$benutzer_result = $this->_ci->BenutzerModel->load(array($new_uid));
$benutzer_result = $this->_ci->BenutzerModel->load(array(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid));
if (isError($benutzer_result)) return $benutzer_result;
@@ -250,23 +227,12 @@ class LektorLib
'bismelden'
);
$nullable_fields = array('stundensatz', 'semesterstunden', 'planstunden');
$updateData = array();
foreach ($updatableFields as $field)
{
if (!array_key_exists($field, $new_data))
{
continue;
}
$value = isset($new_data[$field]) ? $new_data[$field] : null;
$value = $new_data[$field];
if (in_array($field, $nullable_fields))
{
$updateData[$field] = $value;
}
elseif ($value !== null)
if ($value !== null)
{
$updateData[$field] = $value;
}
@@ -274,24 +240,10 @@ class LektorLib
$updateData['updatevon'] = getAuthUID();
$updateData['updateamum'] = date('Y-m-d H:i:s');
$result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid), $updateData);
$result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid), $updateData);
if (isError($result)) return $result;
if ($stundenplan_update)
{
$update_result = $this->_ci->StundenplandevModel->update([
'lehreinheit_id' => $lehreinheit_id,
'mitarbeiter_uid' => $old_uid,
], [
'mitarbeiter_uid' => $new_uid,
'updateamum' => date('Y-m-d H:i:s'),
'updatevon' => getAuthUID()
]);
if (isError($update_result)) return $update_result;
}
if ($warning !== '') return success(['warning' => $warning]);
return success('Erfolgreich geupdated');
@@ -395,52 +347,4 @@ class LektorLib
$this->_ci->LehreinheitmitarbeiterModel->addGroupBy('tbl_studiengang.bezeichnung');
return $this->_ci->LehreinheitmitarbeiterModel->load();
}
private function hasKollision($stunden, $mitarbeiter)
{
$kollision = array();
$ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre');
$ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung');
foreach ($stunden as $stunde)
{
$stundenplan_result = $this->_ci->StundenplandevModel->lektorHasStundenplandevEintrag($mitarbeiter, $stunde->datum, $stunde->stunde);
if (isError($stundenplan_result))
return $stundenplan_result;
if (hasData($stundenplan_result))
{
$stundenplan_result = getData($stundenplan_result)[0];
$kollision[] = "Kollision stundenplandev: $stundenplan_result->stundenplandev_id|$stundenplan_result->lektor|$stundenplan_result->ort_kurzbz|$stundenplan_result->stg_kurzbz-$stundenplan_result->semester$stundenplan_result->verband$stundenplan_result->gruppe$stundenplan_result->gruppe_kurzbz - $stundenplan_result->datum/$stundenplan_result->stunde";
}
else
{
if ($ignore_zeitsperre == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER))))
{
$zeitsperre_result = $this->_ci->ZeitsperreModel->checkIfZeitsperreExists($mitarbeiter, $stunde->datum, $stunde->stunde);
if (hasData($zeitsperre_result))
{
$zeitsperre_result = getData($zeitsperre_result)[0];
$kollision[] = "Kollision (Zeitsperre): $zeitsperre_result->zeitsperre_id|$zeitsperre_result->mitarbeiter_uid|$zeitsperre_result->zeitsperretyp_kurzbz - $zeitsperre_result->vondatum/$zeitsperre_result->vonstunde|$zeitsperre_result->bisdatum/$zeitsperre_result->bisstunde";
}
}
if ($ignore_reservierung == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER))))
{
$reservierung_result = $this->_ci->ReservierungModel->lektorHasReservierung($mitarbeiter, $stunde->datum, $stunde->stunde);
if (hasData($reservierung_result))
{
$reservierung_result = getData($reservierung_result)[0];
$kollision[] = "Kollision (Reservierung): $reservierung_result->reservierung_id|$reservierung_result->uid|$reservierung_result->ort_kurzbz|$reservierung_result->stg_kurzbz-$reservierung_result->semester$reservierung_result->verband$reservierung_result->gruppe$reservierung_result->gruppe_kurzbz - $reservierung_result->datum/$reservierung_result->stunde";
}
}
}
}
return isEmptyArray($kollision) ? false : $kollision;
}
}
-15
View File
@@ -181,21 +181,6 @@ class StundenplanLib
return success($stundenplan_data);
}
public function getEventsByLE($lehreinheit_id, $start, $end, $stundenplan)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
return $this->_ci->StundenplanModel->getStundenplanLE($lehreinheit_id, $start, $end, $stundenplan);
}
public function getEventsByLV($lehrveranstaltung_id, $start, $end, $stundenplan)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
return $this->_ci->StundenplanModel->getStundenplanLV($lehrveranstaltung_id, $start, $end, $stundenplan);
}
/**
* Get stundenplan for a room
*
@@ -40,7 +40,9 @@ abstract class AbstractBestandteil implements IValidation
if( is_bool($new_value) && ($old_value !== $new_value) ) {
$this->modifiedcolumns[$columnname] = $columnname;
} else if($old_value != $new_value) {
} else if(is_null($old_value) xor is_null($new_value)) {
$this->modifiedcolumns[$columnname] = $columnname;
} else if($old_value != $new_value) {
$this->modifiedcolumns[$columnname] = $columnname;
}
}
@@ -3,6 +3,7 @@ namespace vertragsbestandteil;
use Exception;
use vertragsbestandteil\VertragsbestandteilStunden;
use vertragsbestandteil\VertragsbestandteilLohnguide;
/**
* Description of VertragsbestandteilFactory
@@ -22,6 +23,7 @@ class VertragsbestandteilFactory
const VERTRAGSBESTANDTEIL_URLAUBSANSPRUCH = 'urlaubsanspruch';
const VERTRAGSBESTANDTEIL_ZEITAUFZEICHNUNG = 'zeitaufzeichnung';
const VERTRAGSBESTANDTEIL_LEHRE = 'lehre';
const VERTRAGSBESTANDTEIL_LOHNGUIDE = 'lohnguide';
public static function getVertragsbestandteil($data, $fromdb=false)
{
@@ -69,6 +71,11 @@ class VertragsbestandteilFactory
$vertragsbestandteil = new VertragsbestandteilZeitaufzeichnung();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$vertragsbestandteil = new VertragsbestandteilLohnguide();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
default:
throw new Exception('Unknown vertragsbestandteiltyp_kurzbz '
@@ -127,6 +134,12 @@ class VertragsbestandteilFactory
$vertragsbestandteildbmodel = $CI->VertragsbestandteilZeitaufzeichnung_model;
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$CI->load->model('vertragsbestandteil/VertragsbestandteilLohnguide_model',
'VertragsbestandteilLohnguide_model');
$vertragsbestandteildbmodel = $CI->VertragsbestandteilLohnguide_model;
break;
default:
throw new Exception('Unknown vertragsbestandteil_kurzbz '
. $vertragsbestandteil_kurzbz);
@@ -10,6 +10,7 @@ require_once __DIR__ . '/VertragsbestandteilKuendigungsfrist.php';
require_once __DIR__ . '/VertragsbestandteilUrlaubsanspruch.php';
require_once __DIR__ . '/VertragsbestandteilFreitext.php';
require_once __DIR__ . '/VertragsbestandteilKarenz.php';
require_once __DIR__ . '/VertragsbestandteilLohnguide.php';
require_once __DIR__ . '/VertragsbestandteilFactory.php';
require_once __DIR__ . '/OverlapChecker.php';
@@ -0,0 +1,161 @@
<?php
namespace vertragsbestandteil;
use vertragsbestandteil\Vertragsbestandteil;
use vertragsbestandteil\VertragsbestandteilFactory;
class VertragsbestandteilLohnguide extends Vertragsbestandteil
{
protected $stellenbezeichnung;
protected $vordienstzeit;
protected $fachrichtung_kurzbz;
protected $modellstelle_kurzbz;
protected $kommentar_person;
protected $kommentar_modellstelle;
public function __construct()
{
parent::__construct();
$this->setVertragsbestandteiltyp_kurzbz(
VertragsbestandteilFactory::VERTRAGSBESTANDTEIL_LOHNGUIDE);
}
public function getStellenbezeichnung()
{
return $this->stellenbezeichnung;
}
public function setStellenbezeichnung($stellenbezeichnung): self
{
$this->markDirty('stellenbezeichnung', $this->stellenbezeichnung, $stellenbezeichnung);
$this->stellenbezeichnung = $stellenbezeichnung;
return $this;
}
public function getVordienstzeit()
{
return $this->vordienstzeit;
}
public function setVordienstzeit($vordienstzeit): self
{
$this->markDirty('vordienstzeit', $this->vordienstzeit, $vordienstzeit);
$this->vordienstzeit = $vordienstzeit;
return $this;
}
public function getFachrichtung_kurzbz()
{
return $this->fachrichtung_kurzbz;
}
public function setFachrichtung_kurzbz($fachrichtung_kurzbz): self
{
$this->markDirty('fachrichtung_kurzbz', $this->fachrichtung_kurzbz, $fachrichtung_kurzbz);
$this->fachrichtung_kurzbz = $fachrichtung_kurzbz;
return $this;
}
public function getModellstelle_kurzbz()
{
return $this->modellstelle_kurzbz;
}
public function setModellstelle_kurzbz($modellstelle_kurzbz): self
{
$this->markDirty('modellstelle_kurzbz', $this->modellstelle_kurzbz, $modellstelle_kurzbz);
$this->modellstelle_kurzbz = $modellstelle_kurzbz;
return $this;
}
public function getKommentar_person()
{
return $this->kommentar_person;
}
public function setKommentar_person($kommentar_person): self
{
$this->markDirty('kommentar_person', $this->kommentar_person, $kommentar_person);
$this->kommentar_person = $kommentar_person;
return $this;
}
public function getKommentar_modellstelle()
{
return $this->kommentar_modellstelle;
}
public function setKommentar_modellstelle($kommentar_modellstelle): self
{
$this->markDirty('kommentar_modellstelle', $this->kommentar_modellstelle, $kommentar_modellstelle);
$this->kommentar_modellstelle = $kommentar_modellstelle;
return $this;
}
public function hydrateByStdClass($data, $fromdb=false)
{
parent::hydrateByStdClass($data, $fromdb);
$this->fromdb = $fromdb;
isset($data->fachrichtung_kurzbz) && $this->setFachrichtung_kurzbz($data->fachrichtung_kurzbz);
isset($data->stellenbezeichnung) && $this->setStellenbezeichnung($data->stellenbezeichnung);
isset($data->vordienstzeit) && $this->setVordienstzeit($data->vordienstzeit);
isset($data->modellstelle_kurzbz) && $this->setModellstelle_kurzbz($data->modellstelle_kurzbz);
isset($data->kommentar_person) && $this->setKommentar_person($data->kommentar_person);
isset($data->kommentar_modellstelle) && $this->setKommentar_modellstelle($data->kommentar_modellstelle);
$this->fromdb = false;
}
public function toStdClass(): \stdClass
{
$tmp = array(
'vertragsbestandteil_id' => $this->getVertragsbestandteil_id(),
'stellenbezeichnung' => $this->getStellenbezeichnung(),
'vordienstzeit' => $this->getVordienstzeit(),
'fachrichtung_kurzbz' => $this->getFachrichtung_kurzbz(),
'modellstelle_kurzbz' => $this->getModellstelle_kurzbz(),
'kommentar_person' => $this->getKommentar_person(),
'kommentar_modellstelle' => $this->getKommentar_modellstelle(),
);
$tmp = array_filter($tmp, function($k) {
return in_array($k, $this->modifiedcolumns);
}, ARRAY_FILTER_USE_KEY);
return (object) $tmp;
}
public function __toString()
{
$txt = <<<EOTXT
modellstelle_kurzbz: {$this->getModellstelle_kurzbz()}
EOTXT;
return parent::__toString() . $txt;
}
public function validate()
{
$value = $this->vordienstzeit;
if ($value === null || $value === '') {
$result = null; // allow null value
} else {
$result = filter_var($value, FILTER_VALIDATE_INT, [
'options' => [
'min_range' => 0,
'max_range' => 100
]
]);
if ($result === false) {
$this->validationerrors[] = 'Vordienstjahre muss eine ganze Zahl (0 bis 100) enthalten oder leer sein.';
}
}
return parent::validate();
}
}
@@ -52,53 +52,4 @@ class LePruefung_model extends DB_Model
'student_uid' => $student_uid
]);
}
public function getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz) {
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_pruefung.note = tbl_note.note
AND tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz
AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
ORDER BY datum DESC;";
return $this->execReadOnlyQuery($qry, array($lv_id, $sem_kurzbz));
}
public function getPruefungenByUidTypLvStudiensemester($uid, $typ = null, $lv_id = null, $sem_kurzbz = null) {
$params = [$uid];
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE student_uid= ?
AND tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_pruefung.note = tbl_note.note
AND tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz";
if ($typ != null)
{
$qry .= " AND tbl_pruefungstyp.pruefungstyp_kurzbz = ?";
$params[] = $typ;
}
if ($lv_id != null)
{
$qry .= " AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?";
$params[] = $lv_id;
}
if ($sem_kurzbz != null)
{
$qry .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?";
$params[] = $sem_kurzbz;
}
$qry .= " ORDER BY datum DESC";
return $this->execReadOnlyQuery($qry, $params);
}
}
@@ -314,28 +314,6 @@ EOSQL;
return $this->execQuery($query, $params);
}
public function getAllLehreinheitenForLvaAndMaUid($lva_id, $ma_uid, $sem_kurzbz)
{
$query = "SELECT DISTINCT tbl_lehreinheitmitarbeiter.lehreinheit_id, tbl_lehreinheit.lehrveranstaltung_id, tbl_lehreinheit.lehrform_kurzbz,
tbl_lehreinheitmitarbeiter.mitarbeiter_uid,
tbl_lehreinheitgruppe.semester,
tbl_lehreinheitgruppe.verband,
tbl_lehreinheitgruppe.gruppe,
tbl_lehreinheitgruppe.gruppe_kurzbz,
tbl_lehrveranstaltung.kurzbz,
tbl_studiengang.kurzbzlang,
(SELECT COUNT(DISTINCT datum) FROM campus.vw_stundenplan WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as termincount,
(SELECT COUNT(*) FROM campus.vw_student_lehrveranstaltung WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as studentcount
FROM lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON (tbl_lehreinheitgruppe.studiengang_kz = tbl_studiengang.studiengang_kz)
WHERE lehrveranstaltung_id = ? AND studiensemester_kurzbz = ? AND mitarbeiter_uid = ?
ORDER BY tbl_lehreinheitgruppe.gruppe_kurzbz";
return $this->execQuery($query, [$lva_id, $sem_kurzbz, $ma_uid]);
}
public function getOes($lehreinheit_id)
{
@@ -341,7 +341,7 @@ class Lehreinheitgruppe_model extends DB_Model
$this->db->where('lehreinheit_id', $lehreinheit_id);
$this->db->where('studiengang_kz', $gruppen_array->studiengang_kz);
if (!isEmptyString((string)$gruppen_array->semester))
if (!isEmptyString($gruppen_array->semester))
{
$this->db->where('semester', $gruppen_array->semester);
}
@@ -444,37 +444,30 @@ class Lehreinheitgruppe_model extends DB_Model
)
ELSE tbl_gruppe.beschreibung
END AS beschreibung");
$this->addSelect("
CASE
WHEN trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, '')) = '' THEN
$this->addSelect("CASE
WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN
(
SELECT EXISTS (
SELECT 1
FROM lehre.tbl_stundenplandev
WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
AND semester = tbl_lehreinheitgruppe.semester
AND TRIM(COALESCE(verband, '')) = TRIM(tbl_lehreinheitgruppe.verband)
AND TRIM(COALESCE(gruppe, '')) = TRIM(tbl_lehreinheitgruppe.gruppe)
AND (gruppe_kurzbz IS NULL OR gruppe_kurzbz = '')
)
)
ELSE
(
SELECT EXISTS (
SELECT 1
FROM lehre.tbl_stundenplandev sp
WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
AND sp.semester = tbl_lehreinheitgruppe.semester
AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, ''))
AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, ''))
AND trim(COALESCE(sp.gruppe_kurzbz, '')) = ''
FROM lehre.tbl_stundenplandev
WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz
)
)
ELSE
(
SELECT EXISTS (
SELECT 1
FROM lehre.tbl_stundenplandev sp
WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id
AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz
AND sp.semester = tbl_lehreinheitgruppe.semester
AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, ''))
AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, ''))
AND trim(COALESCE(sp.gruppe_kurzbz, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, ''))
)
)
END AS verplant
");
END AS verplant");
$this->addJoin('tbl_studiengang', 'studiengang_kz', 'LEFT');
$this->addJoin('public.tbl_gruppe', 'gruppe_kurzbz', 'LEFT');
@@ -317,9 +317,7 @@ class Lehrveranstaltung_model extends DB_Model
tbl_bisio.bisio_id, tbl_bisio.von, tbl_bisio.bis, tbl_student.studiengang_kz AS stg_kz_student,
tbl_zeugnisnote.note, tbl_mitarbeiter.mitarbeiter_uid, tbl_person.matr_nr, tbl_benutzer.uid,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as kuerzel, tbl_studiengang.orgform_kurzbz, vw_student_lehrveranstaltung.semester, vw_student_lehrveranstaltung.studiensemester_kurzbz, vw_student_lehrveranstaltung.bezeichnung,
tbl_student.prestudent_id,
campus.vw_student_lehrveranstaltung.lehreinheit_id
tbl_student.prestudent_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -1056,26 +1054,6 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($qry, $params);
}
public function getLvForLektorInSemester($sem_kurzbz, $uid) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
(SELECT kurzbz FROM public.tbl_mitarbeiter
WHERE mitarbeiter_uid=tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as lektor
FROM
lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung as lehrfach ON(tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($sem_kurzbz, $uid));
}
public function getLvsByOrganization($oe_kurzbz)
{
$qry="
@@ -1267,7 +1245,7 @@ class Lehrveranstaltung_model extends DB_Model
{
return "
SELECT
distinct on (lehrveranstaltung_id) lehrveranstaltung_id, tbl_lehrveranstaltung.kurzbz as lv_kurzbz, tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, bezeichnung_english as lv_bezeichnung_english, studiengang_kz,
lehrveranstaltung_id, tbl_lehrveranstaltung.kurzbz as lv_kurzbz, tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, bezeichnung_english as lv_bezeichnung_english, studiengang_kz,
tbl_studienplan_lehrveranstaltung.semester, tbl_lehrveranstaltung.sprache,
ects as lv_ects, semesterstunden, anmerkung, lehre, lehreverzeichnis as lv_lehreverzeichnis, tbl_lehrveranstaltung.aktiv,
planfaktor as lv_planfaktor, planlektoren as lv_planlektoren, planpersonalkosten as lv_planpersonalkosten,
@@ -29,14 +29,10 @@ class Lvgesamtnote_model extends DB_Model
$this->addSelect("lv.bezeichnung AS lehrveranstaltung_bezeichnung");
$this->addSelect("lv.studiengang_kz");
$this->addSelect("UPPER(stg.typ || stg.kurzbz) AS studiengang");
$this->addSelect("person.vorname");
$this->addSelect("person.nachname");
$this->addJoin("lehre.tbl_note n", "note");
$this->addJoin("lehre.tbl_lehrveranstaltung lv", "lehrveranstaltung_id");
$this->addJoin("public.tbl_studiengang stg", "studiengang_kz");
$this->addJoin("public.tbl_benutzer benutzer", "uid = student_uid", "LEFT");
$this->addJoin("public.tbl_person person", "person_id", "LEFT");
$this->db->where($this->dbTable . ".freigabedatum <", "NOW()", false);
@@ -79,10 +79,10 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person USING (person_id)
WHERE (campus.tbl_paabgabe.insertamum >= NOW() - INTERVAL ?
OR campus.tbl_paabgabe.updateamum >= NOW() - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
WHERE (campus.tbl_paabgabe.insertamum::date = CURRENT_DATE - INTERVAL ?
OR campus.tbl_paabgabe.updateamum::date = CURRENT_DATE - INTERVAL ?)
AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
return $this->execQuery($query, [$interval, $interval, $relevantTypes]);
}
@@ -108,7 +108,7 @@ class Paabgabe_model extends DB_Model
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
WHERE campus.tbl_paabgabe.abgabedatum IS NOT NULL
AND campus.tbl_paabgabe.abgabedatum >= NOW() - INTERVAL ?";
AND campus.tbl_paabgabe.abgabedatum = CURRENT_DATE - INTERVAL ?";
if($relevantTypes !== null) {
$query .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
@@ -149,8 +149,6 @@ class Projektarbeit_model extends DB_Model
lehre.tbl_projektarbeit.abstract as abstract,
lehre.tbl_projektarbeit.abstract_en as abstract_en,
lehre.tbl_projektarbeit.insertamum as insertamum,
(SELECT abgeschicktvon FROM extension.tbl_projektarbeitsbeurteilung WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id AND betreuer_person_id = tbl_projektbetreuer.person_id) AS babgeschickt,
(SELECT abgeschicktvon FROM extension.tbl_projektarbeitsbeurteilung WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter') LIMIT 1) AS zweitbetreuer_abgeschickt,
(SELECT datum FROM campus.tbl_paabgabe WHERE paabgabetyp_kurzbz = 'end' AND abgabedatum IS NOT NULL AND projektarbeit_id = tbl_projektarbeit.projektarbeit_id LIMIT 1) AS abgegeben
FROM lehre.tbl_projektarbeit
@@ -169,7 +167,7 @@ class Projektarbeit_model extends DB_Model
(projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
AND betreuerart_kurzbz IN ('Betreuer', 'Begutachter', 'Erstbegutachter', 'Senatsvorsitz')) as base
ORDER BY insertamum DESC";
return $this->execReadOnlyQuery($betreuerQuery, array($studentUID));
}
@@ -218,16 +216,13 @@ class Projektarbeit_model extends DB_Model
}
public function getProjektbetreuerAnrede($bperson_id) {
$qry_betr="SELECT distinct trim(COALESCE(titelpre,'')||' '||COALESCE(vorname,'')||' '||COALESCE(nachname,'')||' '||COALESCE(titelpost,'')) as first,
public.tbl_mitarbeiter.mitarbeiter_uid, anrede
FROM public.tbl_person JOIN lehre.tbl_projektbetreuer ON(lehre.tbl_projektbetreuer.person_id=public.tbl_person.person_id)
JOIN public.tbl_benutzer ON(public.tbl_benutzer.person_id=public.tbl_person.person_id)
JOIN public.tbl_mitarbeiter ON(public.tbl_benutzer.uid=public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE public.tbl_person.person_id= ?";
$qry_betr="SELECT DISTINCT ON(public.tbl_person.person_id) trim(COALESCE(titelpre,'')||' '||COALESCE(vorname,'')||' '||COALESCE(nachname,'')||' '||COALESCE(titelpost,'')) as first, anrede
FROM public.tbl_person JOIN lehre.tbl_projektbetreuer ON(lehre.tbl_projektbetreuer.person_id=public.tbl_person.person_id)
WHERE public.tbl_person.person_id= ?";
return $this->execReadOnlyQuery($qry_betr, [$bperson_id]);
}
public function getProjektbetreuerEmail($projektarbeit_id) {
$qry = "SELECT (
SELECT kontakt
@@ -249,7 +244,7 @@ class Projektarbeit_model extends DB_Model
return $this->execReadOnlyQuery($qry, [$projektarbeit_id]);
}
public function getProjektbetreuerEmailByPersonID($person_id) {
$qry = "SELECT (
SELECT kontakt
@@ -343,298 +338,139 @@ class Projektarbeit_model extends DB_Model
return $this->execReadOnlyQuery($qry, array($projektarbeit_id));
}
public function getProjektarbeitenForStudiengang($studiengang_kz, $benotet) {
$new_qry = "SELECT DISTINCT ON(tmp.projektarbeit_id) *, campus.get_betreuer_details(tmp.zweitbetreuer_person_id) as zweitbetreuer_full_name, campus.get_betreuer_details(tmp.betreuer_person_id) as erstbetreuer_full_name
FROM(
SELECT
DISTINCT ON(tbl_projektarbeit.projektarbeit_id)
tbl_projektarbeit.projekttyp_kurzbz,
tbl_projektarbeit.titel,
tbl_projektarbeit.projektarbeit_id,
tbl_studiengang.typ, tbl_studiengang.kurzbz,
student_benutzer.uid as student_uid,
student_person.vorname as student_vorname,
student_person.nachname as student_nachname,
tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz,
betreuer_benutzer.uid as betreuer_benutzer_uid,
betreuer_person.titelpre as betreuer_titelpre,
betreuer_person.vorname as betreuer_vorname,
betreuer_person.nachname as betreuer_nachname,
betreuer_person.titelpost as betreuer_titelpost,
lehre.tbl_projektbetreuer.betreuerart_kurzbz as betreuerart,
lehre.tbl_projektbetreuer.person_id as betreuer_person_id,
lehre.tbl_projektarbeit.sprache as sprache,
lehre.tbl_projektarbeit.seitenanzahl as seitenanzahl,
lehre.tbl_projektarbeit.kontrollschlagwoerter as kontrollschlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter as schlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter_en as schlagwoerter_en,
lehre.tbl_projektarbeit.abstract as abstract,
lehre.tbl_projektarbeit.abstract_en as abstract_en,
lehre.tbl_projektarbeit.insertamum as insertamum,
lehre.tbl_projektarbeit.note as note,
(
SELECT orgform_kurzbz
FROM tbl_prestudentstatus
WHERE prestudent_id = (SELECT prestudent_id
FROM tbl_student
WHERE student_uid = student_benutzer.uid
LIMIT 1)
ORDER BY datum DESC, insertamum DESC, ext_id DESC
LIMIT 1
)
as organisationsform,
(
SELECT person_id
FROM lehre.tbl_projektbetreuer
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
AS zweitbetreuer_person_id,
(
SELECT betreuerart_kurzbz
FROM lehre.tbl_projektbetreuer
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
AS zweitbetreuer_betreuerart_kurzbz,
(
SELECT tbl_betreuerart.beschreibung
FROM lehre.tbl_projektbetreuer
JOIN lehre.tbl_betreuerart USING (betreuerart_kurzbz)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
AS zweitbetreuer_betreuerart_beschreibung,
(
SELECT trim(COALESCE(titelpre, '') || ' ' || COALESCE(vorname, '') || ' ' || COALESCE(nachname, '') || ' ' ||
COALESCE(titelpost, ''))
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_full_name,
(
SELECT titelpre
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_titelpre,
(
SELECT vorname
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_vorname,
(
SELECT nachname
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_nachname,
(
SELECT titelpost
FROM public.tbl_person
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
LIMIT 1
)
as zweitbetreuer_titelpost,
(
SELECT
COALESCE(tbl_studienplan.orgform_kurzbz,
tbl_prestudentstatus.orgform_kurzbz, tbl_studiengang.orgform_kurzbz) as
orgform
FROM
public.tbl_prestudent
JOIN public.tbl_prestudentstatus USING(prestudent_id)
JOIN public.tbl_studiensemester USING(studiensemester_kurzbz)
JOIN public.tbl_studiengang USING(studiengang_kz)
LEFT JOIN lehre.tbl_studienplan USING(studienplan_id)
WHERE
prestudent_id=tbl_student.prestudent_id
ORDER BY tbl_prestudentstatus.datum DESC LIMIT 1
) as orgform,
(SELECT status_kurzbz FROM public.tbl_prestudentstatus
WHERE prestudent_id=tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as studienstatus
FROM lehre.tbl_projektarbeit
LEFT JOIN public.tbl_benutzer student_benutzer ON (student_benutzer.uid = lehre.tbl_projektarbeit.student_uid)
LEFT JOIN public.tbl_person student_person ON (student_benutzer.person_id = student_person.person_id)
LEFT JOIN public.tbl_student on(student_benutzer.uid = public.tbl_student.student_uid)
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON (public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz)
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
LEFT JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
LEFT JOIN public.tbl_person betreuer_person ON (betreuer_person.person_id = lehre.tbl_projektbetreuer.person_id)
LEFT JOIN public.tbl_benutzer betreuer_benutzer ON (betreuer_person.person_id = betreuer_benutzer.person_id)
WHERE (projekttyp_kurzbz = 'Bachelor' OR projekttyp_kurzbz = 'Diplom')
AND student_benutzer.aktiv AND (
lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Erstbegutachter'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Begutachter'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Betreuer'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Erstbetreuer'
OR lehre.tbl_projektbetreuer.betreuerart_kurzbz = 'Senatsvorsitz'
)
AND public.tbl_studiengang.studiengang_kz = ?";
if($benotet == 0) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NULL ";
} else if ($benotet == 1) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NOT NULL ";
}
$new_qry .= " ORDER BY tbl_projektarbeit.projektarbeit_id DESC, student_person.nachname ASC
$new_qry = "WITH secondary_betreuer AS (
SELECT DISTINCT ON (pb.projektarbeit_id)
pb.projektarbeit_id,
pb.person_id AS zweitbetreuer_person_id,
pb.betreuerart_kurzbz AS zweitbetreuer_betreuerart_kurzbz,
ba.beschreibung AS zweitbetreuer_betreuerart_beschreibung,
p.titelpre AS zweitbetreuer_titelpre,
p.vorname AS zweitbetreuer_vorname,
p.nachname AS zweitbetreuer_nachname,
p.titelpost AS zweitbetreuer_titelpost,
trim(
COALESCE(p.titelpre, '') || ' ' ||
COALESCE(p.vorname, '') || ' ' ||
COALESCE(p.nachname, '') || ' ' ||
COALESCE(p.titelpost, '')
) AS zweitbetreuer_full_name
FROM lehre.tbl_projektbetreuer pb
JOIN public.tbl_person p ON p.person_id = pb.person_id
LEFT JOIN public.tbl_benutzer b ON b.person_id = p.person_id
LEFT JOIN lehre.tbl_betreuerart ba ON ba.betreuerart_kurzbz = pb.betreuerart_kurzbz
WHERE pb.betreuerart_kurzbz = ANY('{Zweitbetreuer,Zweitbegutachter,Senatsmitglied}')
ORDER BY pb.projektarbeit_id -- DISTINCT ON needs this to be deterministic
)
SELECT DISTINCT ON (tmp.projektarbeit_id)
*,
campus.get_betreuer_details(tmp.zweitbetreuer_person_id) AS zweitbetreuer_full_name,
campus.get_betreuer_details(tmp.betreuer_person_id) AS erstbetreuer_full_name
FROM (
SELECT DISTINCT ON (tbl_projektarbeit.projektarbeit_id)
tbl_projektarbeit.projekttyp_kurzbz,
tbl_projektarbeit.titel,
tbl_projektarbeit.projektarbeit_id,
tbl_studiengang.typ,
tbl_studiengang.kurzbz,
student_benutzer.uid AS student_uid,
student_person.vorname AS student_vorname,
student_person.nachname AS student_nachname,
public.tbl_student.matrikelnr,
tbl_lehreinheit.studiensemester_kurzbz,
betreuer_benutzer.uid AS betreuer_benutzer_uid,
betreuer_person.titelpre AS betreuer_titelpre,
betreuer_person.vorname AS betreuer_vorname,
betreuer_person.nachname AS betreuer_nachname,
betreuer_person.titelpost AS betreuer_titelpost,
lehre.tbl_projektbetreuer.betreuerart_kurzbz AS betreuerart,
lehre.tbl_projektbetreuer.person_id AS betreuer_person_id,
lehre.tbl_projektarbeit.sprache,
lehre.tbl_projektarbeit.seitenanzahl,
lehre.tbl_projektarbeit.kontrollschlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter,
lehre.tbl_projektarbeit.schlagwoerter_en,
lehre.tbl_projektarbeit.abstract,
lehre.tbl_projektarbeit.abstract_en,
lehre.tbl_projektarbeit.insertamum,
lehre.tbl_projektarbeit.note,
sb.zweitbetreuer_person_id,
sb.zweitbetreuer_betreuerart_kurzbz,
sb.zweitbetreuer_betreuerart_beschreibung,
sb.zweitbetreuer_full_name,
sb.zweitbetreuer_titelpre,
sb.zweitbetreuer_vorname,
sb.zweitbetreuer_nachname,
sb.zweitbetreuer_titelpost,
(
SELECT orgform_kurzbz
FROM public.tbl_prestudentstatus
WHERE prestudent_id = (
SELECT prestudent_id FROM public.tbl_student
WHERE student_uid = student_benutzer.uid LIMIT 1
)
ORDER BY datum DESC, insertamum DESC, ext_id DESC
LIMIT 1
) AS organisationsform,
(
SELECT COALESCE(tbl_studienplan.orgform_kurzbz,
tbl_prestudentstatus.orgform_kurzbz,
tbl_studiengang.orgform_kurzbz)
FROM public.tbl_prestudent
JOIN public.tbl_prestudentstatus USING (prestudent_id)
JOIN public.tbl_studiensemester USING (studiensemester_kurzbz)
JOIN public.tbl_studiengang USING (studiengang_kz)
LEFT JOIN lehre.tbl_studienplan USING (studienplan_id)
WHERE prestudent_id = public.tbl_student.prestudent_id
ORDER BY tbl_prestudentstatus.datum DESC
LIMIT 1
) AS orgform,
(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus
WHERE prestudent_id = public.tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC
LIMIT 1
) AS studienstatus
FROM lehre.tbl_projektarbeit
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
LEFT JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
LEFT JOIN public.tbl_person betreuer_person ON betreuer_person.person_id = lehre.tbl_projektbetreuer.person_id
LEFT JOIN public.tbl_benutzer betreuer_benutzer ON betreuer_person.person_id = betreuer_benutzer.person_id
LEFT JOIN secondary_betreuer sb ON sb.projektarbeit_id = tbl_projektarbeit.projektarbeit_id -- ← THE NEW LINE
WHERE (projekttyp_kurzbz = 'Bachelor' OR projekttyp_kurzbz = 'Diplom')
AND student_benutzer.aktiv
AND lehre.tbl_projektbetreuer.betreuerart_kurzbz IN (
'Erstbegutachter', 'Begutachter', 'Betreuer', 'Erstbetreuer', 'Senatsvorsitz'
)
AND public.tbl_studiengang.studiengang_kz = ?";
if($benotet == 0) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NULL ";
} else if ($benotet == 1) {
$new_qry .= " AND lehre.tbl_projektarbeit.note IS NOT NULL ";
}
$new_qry .= " ORDER BY tbl_projektarbeit.projektarbeit_id DESC, student_person.nachname ASC
) as tmp";
return $this->execReadOnlyQuery($new_qry, array($studiengang_kz));
}
/**
* Prüft ob Projektarbeit aktuell ist (also zurzeit online bewertet wird).
* @param $projektarbeit_id
* @return boolean
*/
public function projektarbeitIsCurrent($projektarbeit_id) {
$version = $this->getVersion($projektarbeit_id);
// paarbeit sollte nur ab einem Studiensemester online bewertet werden
return $version === null ? null : $version->isCurrent;
}
/**
* Holt sich Version der Projektarbeit.
* Liefert auch mit, ob die Version die aktuellste ist.
* z.B.: Masterarbeiten waren ab der Änderung zur Gewichtung der Punkte aktuell,
* Bachelorarbeiten waren ab dem Umstieg auf das Online Beurteilungsformular aktuell.
* @param $projektarbeit_id
* @return objekt mit Versionsinfo, null im Fehlerfall
*/
private function getVersion($projektarbeit_id) {
$_versions_query = array(
'Diplom' => array(
'SS2025',
'SS2023',
'SS2022'
),
'Others' => array(
'SS2025',
'SS2022',
)
);
$_versions_check = array(
'Diplom' => array(
'SS2025' => 3,
'SS2023' => 2,
'SS2022' => 1
),
'Others' => array(
'SS2025' => 2,
'SS2022' => 1
)
);
// paarbeit sollte nur ab einem Studiensemester online bewertet werden
$qry="
SELECT
CASE
WHEN semesters_diplom.studiensemester_kurzbz IS NOT NULL
THEN semesters_diplom.studiensemester_kurzbz
ELSE semesters.studiensemester_kurzbz
END AS version_studiensemester_kurzbz,
pa.projekttyp_kurzbz
FROM
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN public.tbl_studiensemester sem USING(studiensemester_kurzbz)
LEFT JOIN (
SELECT
start, studiensemester_kurzbz
FROM
public.tbl_studiensemester
WHERE
studiensemester_kurzbz IN ?
) semesters ON sem.start >= semesters.start AND pa.projekttyp_kurzbz <> 'Diplom'
LEFT JOIN (
SELECT
start, studiensemester_kurzbz
FROM
public.tbl_studiensemester
WHERE
studiensemester_kurzbz IN ?
) semesters_diplom ON sem.start >= semesters_diplom.start AND pa.projekttyp_kurzbz = 'Diplom'
WHERE
projektarbeit_id = ?
ORDER BY
semesters.start DESC, semesters_diplom.start DESC
LIMIT 1";
$resultociniBambini = $this->execReadOnlyQuery($qry, array($_versions_query['Others'], $_versions_query['Diplom'], $projektarbeit_id));
if(hasData($resultociniBambini)) {
$data = getData($resultociniBambini);
if(count($data) > 0) {
$row = $data[0];
// known project types
if (isset($_versions_check[$row->projekttyp_kurzbz][$row->version_studiensemester_kurzbz]))
{
$row->versionNumber = $_versions_check[$row->projekttyp_kurzbz][$row->version_studiensemester_kurzbz];
$row->isCurrent =
$_versions_check[$row->projekttyp_kurzbz][$row->version_studiensemester_kurzbz]
== max($_versions_check[$row->projekttyp_kurzbz]);
}
elseif (isset($_versions_check['Others'][$row->version_studiensemester_kurzbz]))
{
$row->versionNumber = $_versions_check['Others'][$row->version_studiensemester_kurzbz];
$row->isCurrent =
$_versions_check['Others'][$row->version_studiensemester_kurzbz]
== max($_versions_check['Others']);
}
else
{
$row->isCurrent = false;
$row->versionNumber = 0;
}
return $row;
} else {
return null;
}
} else {
return null;
}
}
/*
*
* @param
* @return object success or error
@@ -306,5 +306,4 @@ class Pruefung_model extends DB_Model
return $this->loadWhereCommitteeExamsFailed();
}
}
@@ -148,7 +148,7 @@ class Zeugnisnote_model extends DB_Model
*
* @return object
*/
public function getZeugnisnoten($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id = null)
public function getZeugnisnoten($student_uid, $studiensemester_kurzbz)
{
$params = array();
$where='';
@@ -163,11 +163,6 @@ class Zeugnisnote_model extends DB_Model
$where.=" AND vw_student_lehrveranstaltung.studiensemester_kurzbz= ?";
$params[] = $studiensemester_kurzbz;
}
if($lehrveranstaltung_id != null)
{
$where .= " AND vw_student_lehrveranstaltung.lehrveranstaltung_id = ?";
$params[] = $lehrveranstaltung_id;
}
$where2='';
@@ -181,11 +176,6 @@ class Zeugnisnote_model extends DB_Model
$where2 .= " AND studiensemester_kurzbz= ?";
$params[] = $studiensemester_kurzbz;
}
if($lehrveranstaltung_id != null)
{
$where2 .=" AND lehrveranstaltung_id = ?";
$params[] = $lehrveranstaltung_id;
}
$qry = "SELECT
a.*,
@@ -198,10 +188,7 @@ class Zeugnisnote_model extends DB_Model
lv.semester AS semester_lv,
lv.ects AS ects_lv,
lv.zeugnis,
lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english,
s.verband,
person.vorname,
person.nachname
lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english
FROM (
SELECT vw_student_lehrveranstaltung.lehrveranstaltung_id, uid,
vw_student_lehrveranstaltung.studiensemester_kurzbz, note, punkte, uebernahmedatum, benotungsdatum,
@@ -244,8 +231,6 @@ class Zeugnisnote_model extends DB_Model
ORDER BY sort
) a
LEFT JOIN public.tbl_student s ON (a.uid = s.student_uid)
LEFT JOIN public.tbl_benutzer benutzer ON benutzer.uid = s.student_uid
LEFT JOIN public.tbl_person person ON benutzer.person_id = person.person_id
LEFT JOIN public.tbl_studiengang stg1 ON (s.studiengang_kz = stg1.studiengang_kz)
LEFT JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang stg2 ON (lv.studiengang_kz = stg2.studiengang_kz)";
@@ -594,7 +594,10 @@ class Studiengang_model extends DB_Model
$this->addSelect('p.prestudent_id');
$this->addSelect('pers.vorname');
$this->addSelect('pers.nachname');
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', " . $this->dbTable . ".bezeichnung, ')') AS name");
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', "
. $this->dbTable . ".bezeichnung, ', ', "
. "UPPER(" . $this->dbTable . ".typ), "
. "UPPER(" . $this->dbTable . ".kurzbz),')') AS name");
$this->addJoin('public.tbl_prestudent p', 'studiengang_kz');
$this->addJoin(
@@ -59,37 +59,6 @@ class Studienplan_model extends DB_Model
'tbl_studienplan_lehrveranstaltung.semester' => $semester
));
}
public function getStudienplanByLvaSemKurzbz($lehrveranstaltung_id, $studiensemester_kurzbz) {
$qry= "
SELECT
DISTINCT tbl_studienplan.*
FROM
lehre.tbl_studienplan
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(studienplan_id)
WHERE
tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (
SELECT
lv.lehrveranstaltung_id
FROM
lehre.tbl_lehrveranstaltung AS lv
LEFT JOIN lehre.tbl_lehrveranstaltung AS t ON t.lehrveranstaltung_id=lv.lehrveranstaltung_template_id
WHERE
lv.lehrtyp_kurzbz<>'tpl'
AND (lv.lehrveranstaltung_id= ? OR (lv.lehrveranstaltung_template_id= ? AND t.lehrtyp_kurzbz='tpl'))
)
AND EXISTS (
SELECT 1
FROM
lehre.tbl_studienplan_semester
WHERE studienplan_id=tbl_studienplan.studienplan_id
AND studiensemester_kurzbz= ?
AND semester = tbl_studienplan_lehrveranstaltung.semester)
ORDER BY bezeichnung";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $lehrveranstaltung_id, $studiensemester_kurzbz));
}
public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id)
{
@@ -206,7 +206,6 @@ class Notiz_model extends DB_Model
person_bearbeiter.vorname, person_bearbeiter.nachname
";
return $this->execQuery($qry, array($type, $id));
}
@@ -135,15 +135,4 @@ class Reservierung_model extends DB_Model
return $this->execQuery($query, [$uid, $uid]);
}
public function lektorHasReservierung($uid, $datum, $stunde)
{
$qry = "SELECT reservierung_id, uid, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde
FROM lehre.vw_reservierung
WHERE uid = ?
AND datum = ?
AND stunde = ?";
return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]);
}
}
@@ -333,68 +333,6 @@ class Stundenplan_model extends DB_Model
", [$start_date, $end_date, $lv_id]);
}
public function getStundenplanLE($lehreinheit, $start_date, $end_date, $stundenplan)
{
$qry = "
WITH lehreinheiten AS (
SELECT lehreinheit_id FROM lehre.tbl_lehreinheit WHERE lehreinheit_id = ?
), " . $this->getStundenplanCTE($stundenplan) . "
SELECT *
FROM stundenplanentries
";
return $this->execReadOnlyQuery($qry, array($lehreinheit, $start_date, $end_date));
}
public function getStundenplanLV($lehrveranstaltung_id, $start_date, $end_date, $stundenplan)
{
$qry = "
WITH lehreinheiten AS (
SELECT lehreinheit_id
FROM lehre.tbl_lehreinheit
JOIN tbl_studiensemester USING(studiensemester_kurzbz)
WHERE lehrveranstaltung_id = ?
AND tbl_studiensemester.start >= ? AND tbl_studiensemester.ende <= ?
), " . $this->getStundenplanCTE($stundenplan) . "
SELECT *
FROM stundenplanentries
";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $start_date, $end_date, $start_date, $end_date));
}
private function getStundenplanCTE($stundenplan)
{
return "entries AS (
SELECT
datum, min(stunde) as stunde_beginn, max(stunde) as stunde_ende,
array_agg(DISTINCT(
CASE WHEN gruppe_kurzbz is not null THEN gruppe_kurzbz
ELSE (UPPER(stg_typ || stg_kurzbz) || COALESCE(semester,'0') || COALESCE(verband,'') || COALESCE(gruppe,''))
END)) as gruppen_kuerzel,
array_agg(DISTINCT CONCAT(vorname || ' ' || nachname)) as lektorname,
array_agg(DISTINCT stundenplan.ort_kurzbz) as ort_kurzbz,
array_agg(DISTINCT titel) as titel,
lehrfach_bez, stundenplan.lehreinheit_id, lehrveranstaltung_id
FROM lehre.vw_$stundenplan as stundenplan
JOIN public.tbl_mitarbeiter ON stundenplan.uid = tbl_mitarbeiter.mitarbeiter_uid
JOIN tbl_benutzer ON tbl_mitarbeiter.mitarbeiter_uid = tbl_benutzer.uid
JOIN tbl_person USING(person_id)
JOIN lehreinheiten ON stundenplan.lehreinheit_id = lehreinheiten.lehreinheit_id
WHERE datum >= ? AND datum <= ?
GROUP BY datum, unr, stundenplan.lehreinheit_id, lehrveranstaltung_id, lehrfach_bez, lehrfach_bez
ORDER BY datum, min(stunde), unr, lehreinheit_id
),
stundenplanentries AS (
SELECT
entries.*,
stundeb.beginn AS beginn,
stundee.ende AS ende
FROM entries
JOIN lehre.tbl_stunde stundeb ON stundeb.stunde = entries.stunde_beginn
JOIN lehre.tbl_stunde stundee ON stundee.stunde = entries.stunde_ende
)";
}
/**
* queries Stundenplan and filters by assigned ma_kurzbz, very similar to get by LVA
*
@@ -241,15 +241,4 @@ class Stundenplandev_model extends DB_Model
return $this->delete(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid));
}
public function lektorHasStundenplandevEintrag($uid, $datum, $stunde)
{
$qry = "SELECT stundenplandev_id, lektor, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde
FROM lehre.vw_stundenplandev
WHERE uid = ?
AND datum = ?
AND stunde = ?";
return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]);
}
}
@@ -61,40 +61,4 @@ class Zeitsperre_model extends DB_Model
return $this->execQuery($qry);
}
public function checkIfZeitsperreExists($uid, $datum, $stunde)
{
$this->db->select("*");
$this->db->where('mitarbeiter_uid', $uid);
$this->db->where('zeitsperretyp_kurzbz !=', 'ZVerfueg');
$this->db->group_start();
$this->db->where('vondatum <', $datum);
$this->db->or_group_start();
$this->db->where('vondatum', $datum);
$this->db->group_start();
$this->db->where('vonstunde <=', $stunde);
$this->db->or_where('vonstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
$this->db->group_start();
$this->db->where('bisdatum >', $datum);
$this->db->or_group_start();
$this->db->where('bisdatum', $datum);
$this->db->group_start();
$this->db->where('bisstunde >=', $stunde);
$this->db->or_where('bisstunde IS NULL', null, false);
$this->db->group_end();
$this->db->group_end();
$this->db->group_end();
return $this->load();
}
}
+6 -4
View File
@@ -242,6 +242,7 @@ class Message_model extends DB_Model
*/
public function getMessagesForTable($person_id, $offset, $limit)
{
$limitoffset = (!is_null($offset) && !is_null($limit)) ? 'limit ? offset ?' : '';
$sql = <<<EOSQL
with filtered_messages as (
select
@@ -310,11 +311,12 @@ class Message_model extends DB_Model
public.tbl_person pr on pr.person_id = fm.recipient_id
order by
m.insertamum DESC
limit ?
offset ?;
{$limitoffset}
EOSQL;
$parametersArray = array($person_id, $person_id, $limit, $offset);
$parametersArray = $limitoffset
? array($person_id, $person_id, $limit, $offset)
: array($person_id, $person_id);
$count = 0;
$data = $this->execQuery($sql, $parametersArray);
@@ -325,7 +327,7 @@ EOSQL;
$data = getData($data);
if($data)
{
$count = ceil($data[0]->total_msgs / $limit);
$count = is_null($limit) ? 1 : ceil($data[0]->total_msgs / $limit);
}
return success(['data' => $data, 'count' => $count]);
@@ -0,0 +1,11 @@
<?php
class VertragsbestandteilLohnguide_model extends DB_Model
{
public function __construct()
{
parent::__construct();
$this->dbTable = 'hr.tbl_vertragsbestandteil_lohnguide';
$this->pk = 'vertragsbestandteil_id';
}
}
@@ -37,7 +37,8 @@ class Vertragsbestandteil_model extends DB_Model
kf.arbeitgeber_frist, kf.arbeitnehmer_frist,
s.wochenstunden, s.teilzeittyp_kurzbz,
u.tage,
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice,
lg.stellenbezeichnung, lg.vordienstzeit, lg.fachrichtung_kurzbz, lg.modellstelle_kurzbz, lg.kommentar_person, lg.kommentar_modellstelle
FROM
hr.tbl_vertragsbestandteil v
LEFT JOIN
@@ -63,6 +64,8 @@ class Vertragsbestandteil_model extends DB_Model
hr.tbl_vertragsbestandteil_urlaubsanspruch u USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_zeitaufzeichnung z USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_lohnguide lg USING(vertragsbestandteil_id)
EOSQL;
return $sql;
}
+1 -1
View File
@@ -38,7 +38,7 @@ $includesArray = array(
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="abgabetoolroot" class="h-100" style="max-width: 95%;" route=<?php echo json_encode($route) ?>
<div id="abgabetoolroot" class="h-100" style="max-width: 99%" route=<?php echo json_encode($route) ?>
uid=<?php echo $uid ?>
student_uid_prop="<?php echo $student_uid_prop ?? '' ?>"
stg_kz_prop="<?php echo $stg_kz_prop ?? '' ?>"
@@ -11,7 +11,6 @@ $includesArray = array(
'skipID' => '#fhccontent',
'vuedatepicker11' => true,
'customCSSs' => array(
'vendor/vuejs/vuedatepicker_css/main.css',
'public/css/components/verticalsplit.css',
'public/css/components/searchbar/searchbar.css',
'public/css/Fhc.css',
@@ -33,17 +32,14 @@ $includesArray = array(
'vendor/npm-asset/primevue/inputnumber/inputnumber.min.js',
'vendor/npm-asset/primevue/speeddial/speeddial.min.js',
'vendor/npm-asset/primevue/textarea/textarea.min.js',
'vendor/moment/luxonjs/luxon.min.js',
'vendor/npm-asset/primevue/password/password.min.js',
'vendor/npm-asset/primevue/multiselect/multiselect.min.js',
'vendor/npm-asset/primevue/timeline/timeline.min.js',
'vendor/npm-asset/primevue/inplace/inplace.min.js',
'vendor/npm-asset/primevue/message/message.min.js',
'vendor/npm-asset/primevue/tieredmenu/tieredmenu.js',
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Dashboard/Fhc.js',
'vendor/olifolkerd/tabulator5/src/js/modules/ColumnCalcs/ColumnCalcs.js'
),
);
-2
View File
@@ -31,8 +31,6 @@
lv-root="<?= site_url('LVVerwaltung'); ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions));?>"
:config="<?= htmlspecialchars(json_encode($configs)); ?>"
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
>
</router-view>
@@ -315,15 +315,22 @@
WHERE tpl.app = '.$APP.'
) pl USING(person_id)
LEFT JOIN (
SELECT DISTINCT ON (tbl_rueckstellung.person_id)
SELECT
tbl_rueckstellung.person_id,
tbl_rueckstellung.datum_bis,
tbl_rueckstellung.status_kurzbz,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung
FROM public.tbl_rueckstellung
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
WHERE tbl_rueckstellung.datum_bis >= NOW()
ORDER BY tbl_rueckstellung.person_id, tbl_rueckstellung.datum_bis DESC
JOIN public.tbl_person sp ON tbl_rueckstellung.person_id = sp.person_id
WHERE tbl_rueckstellung.rueckstellung_id =
(
SELECT srueck.rueckstellung_id
FROM public.tbl_rueckstellung srueck
WHERE srueck.person_id = tbl_rueckstellung.person_id
AND datum_bis >= NOW()
ORDER BY srueck.datum_bis DESC LIMIT 1
)
) rueck ON rueck.person_id = p.person_id
WHERE
EXISTS (
@@ -24,15 +24,22 @@ $query = '
WHERE tpl.app = '.$APP.'
) pl ON p.person_id = pl.person_id
LEFT JOIN (
SELECT DISTINCT ON (tbl_rueckstellung.person_id)
SELECT
tbl_rueckstellung.person_id,
tbl_rueckstellung.datum_bis,
tbl_rueckstellung.status_kurzbz,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung
FROM public.tbl_rueckstellung
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
WHERE tbl_rueckstellung.datum_bis >= NOW()
ORDER BY tbl_rueckstellung.person_id, tbl_rueckstellung.datum_bis DESC
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
JOIN public.tbl_person sp ON tbl_rueckstellung.person_id = sp.person_id
WHERE tbl_rueckstellung.rueckstellung_id =
(
SELECT srueck.rueckstellung_id
FROM public.tbl_rueckstellung srueck
WHERE srueck.person_id = tbl_rueckstellung.person_id
AND datum_bis >= NOW()
ORDER BY srueck.datum_bis DESC LIMIT 1
)
) rueck ON rueck.person_id = p.person_id
WHERE p.person_id NOT IN (SELECT person_id FROM public.tbl_prestudent)';
+5 -5
View File
@@ -41,11 +41,11 @@ ob_start();
<link href="../skin/style.css.php" rel="stylesheet" type="text/css">
<title>Menu</title>
<link href="../skin/flexcrollstyles.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" type="text/css" href="../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../include/js/jquery.ui.datepicker.translation.js"></script>
<link rel="stylesheet" type="text/css" href="../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../include/js/jquery.ui.datepicker.translation.js"></script>
<script type="text/javascript" src="../vendor/jquery/sizzle/sizzle.js"></script>
<script type="text/javascript">
function treemenu(obj)
+6 -4
View File
@@ -46,12 +46,13 @@ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<link rel="stylesheet" href="../../../skin/tablesort.css" type="text/css"/>
<link rel="stylesheet" href="../../../skin/style.css.php" type="text/css">
<link rel="stylesheet" type="text/css" href="../../../skin/jquery-ui-1.9.2.custom.min.css">
<script type="text/javascript" src="../../../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="../../../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="../../../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../../../include/js/jquery.ui.datepicker.translation.js"></script>
<script type="text/javascript" src="../../../vendor/jquery/sizzle/sizzle.js"></script>';
include('../../../include/meta/jquery.php');
include('../../../include/meta/jquery-tablesorter.php');
const MOODLE_ADDON_KURZBZ = 'moodle';
// Load Addons to get Moodle_Path
@@ -71,7 +72,7 @@ echo '
$("#myTable").tablesorter(
{
sortList: [[0,0],[1,0]],
widgets: [\'zebra\']
widgets: [\'zebra\',\'filter\']
});
}
);
@@ -151,8 +152,9 @@ foreach($service->result as $row)
$person = new person();
$person->getPersonFromBenutzer($row->operativ_uid);
$operativ = $person->nachname.' '.$person->vorname;
$oeBez = new organisationseinheit($row->oe_kurzbz);
echo '<tr>';
echo '<td>',$row->oe_kurzbz,'</td>';
echo '<td>',$oeBez->bezeichnung,'</td>';
echo '<td><b>'.$row->bezeichnung.'</b></td>';
echo '<td>',$row->beschreibung,'</td>';
echo '<td><nobr><a href="../profile/index.php?uid='.$row->design_uid.'">',$design,'</a></nobr></td>';
+11 -75
View File
@@ -80,17 +80,9 @@ echo '
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml; charset=UTF-8" />
<link rel="stylesheet" href="../../vendor/components/jqueryui/themes/base/jquery-ui.min.css" type="text/css" />
<link rel="stylesheet" href="../../vendor/twbs/bootstrap3/dist/css/bootstrap.min.css" type="text/css"/>
<link href="../../skin/style.css.php" rel="stylesheet" type="text/css" />
<style>
.ui-dialog-titlebar-close
{
visibility: hidden !important;
}
</style>
<script type="text/javascript" src="../../vendor/components/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../../vendor/components/jqueryui/jquery-ui.min.js"></script>
<script type="text/javascript" src="../../vendor/twbs/bootstrap3/dist/js/bootstrap.min.js"></script>
<script language="Javascript" type="text/javascript">
//<![CDATA[
@@ -139,78 +131,22 @@ echo '
}
}
function GebietStarten(bezeichnung, stunde, minute, sekunde, gebiet_id)
function GebietStarten(bezeichnung,stunde,minute,sekunde,gebiet_id)
{
let message = <?php echo "'".$p->t('testtool/okKlickenUmZuStarten')."'"?> + ' ' + stunde + 'h ' + minute + 'm ' + sekunde + 's';
let title = <?php echo "'".$p->t('testtool/startGebiet')."'"?>;
let abbrechen = <?php echo "'".$p->t('testtool/abbrechen')."'"?>;
if ($('#gebiet-dialog').length === 0)
{
$('body').append(
'<div id="gebiet-dialog" title="' + title + '">' +
'<p id="gebiet-dialog-msg">' + message + '</p>' +
'</div>'
);
var check = confirm(<?php echo "'".$p->t('testtool/okKlickenUmZuStarten')."'"?>+' '+stunde+'h '+minute+'m '+sekunde+'s');
if (check == true) {
var sprache_user = <?php echo "'".$sprache_user."'"?>;
document.location.href = 'frage.php?gebiet_id='+gebiet_id+'&start=true';
}
else {
return false;
}
$('#gebiet-dialog').dialog({
modal: true,
width: 400,
resizable: false,
buttons: [
{
text: 'OK',
click: function() {
$(this).dialog('close');
document.location.href = 'frage.php?gebiet_id=' + gebiet_id + '&start=true';
}
},
{
text: abbrechen,
click: function() {
$(this).dialog('close');
}
}
]
});
}
let letzteFrageBestaetigt = false;
function letzteFrage()
{
if (letzteFrageBestaetigt)
return true;
let message = <?php echo "'".$p->t("testtool/alleFragenBeantwortet")."'"?>;
if ($('#fertig-dialog').length === 0)
{
$('body').append(
'<div id="fertig-dialog">' +
'<p>' + message + '</p>' +
'</div>'
);
}
$('#fertig-dialog').dialog({
modal: true,
width: 400,
resizable: false,
buttons: [
{
text: 'OK',
click: function() {
$(this).dialog('close');
letzteFrageBestaetigt = true;
$('[name="submitantwort"]').click();
}
}
]
});
return false;
alert(<?php echo "'".$p->t("testtool/alleFragenBeantwortet")."'"?>);
return true;
}
$(document).ready(function () {
@@ -711,7 +647,7 @@ if($frage->frage_id!='')
}
$letzte = $frage->getNextFrage($gebiet_id, $_SESSION['pruefling_id'], $frage_id, $demo);
echo "<form action=\"$PHP_SELF?gebiet_id=$gebiet_id&amp;frage_id=$frage->frage_id\" method=\"POST\" ".(!$letzte && !$levelgebiet?"onsubmit=\"return letzteFrage()\"":"").">";
echo "<form action=\"$PHP_SELF?gebiet_id=$gebiet_id&amp;frage_id=$frage->frage_id\" method=\"POST\" ".(!$letzte && !$levelgebiet?"onsubmit=\"letzteFrage()\"":"").">";
echo '
<div class="row text-center">
<table class="table" style="width: 600px; margin-left: auto; margin-right: auto;">
+2 -11
View File
@@ -142,9 +142,7 @@ if (isset($_REQUEST['prestudent']))
}
if ($reihungstest_id != '' && $rt->load($reihungstest_id))
{
$pruefling_exist = new Pruefling();
$alreadyInRT = $pruefling_exist->personAlreadyInRT($ps->person_id, $rt->reihungstest_id, $ps->prestudent_id);
if ($rt->freigeschaltet && !$alreadyInRT)
if ($rt->freigeschaltet)
{
// regenerate Session ID after Login
session_regenerate_id();
@@ -284,14 +282,7 @@ if (isset($_REQUEST['prestudent']))
}
else
{
if ($alreadyInRT)
{
$alertmsg .= '<div class="alert alert-danger">'.$p->t('testtool/reihungstestNichtRegistriert').'</div>';
}
else
{
$alertmsg .= '<div class="alert alert-danger">'.$p->t('testtool/reihungstestNichtFreigeschalten').'</div>';
}
$alertmsg .= '<div class="alert alert-danger">'.$p->t('testtool/reihungstestNichtFreigeschalten').'</div>';
}
}
else
+1 -1
View File
@@ -293,7 +293,7 @@ else if (isset($_SESSION['pruefling_id']))
}
$lastsemester = $row->semester;
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px;">';
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">'. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).'</td></tr>';
}
@@ -342,6 +342,8 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
<vbox>
<checkbox id="mitarbeiter-entwicklungsteam-detail-checkbox-neu" checked="true" hidden="true" />
<textbox id="mitarbeiter-entwicklungsteam-detail-textbox-studiengang" hidden="true" />
<textbox id="mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id" hidden="true" />
<groupbox id="mitarbeiter-entwicklungsteam-detail-groupbox" flex="1">
<caption label="Details" />
<grid id="mitarbeiter-entwicklungsteam-detail-grid" style="margin:4px;" flex="1">
@@ -1708,6 +1708,7 @@ function MitarbeiterEntwicklungsteamSelect()
document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value=studiengang_kz;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value=beginn;
document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value=ende;
document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value=entwicklungsteam_id;
MitarbeiterEntwicklungsteamDetailDisableFields(false);
}
@@ -1725,6 +1726,7 @@ function MitarbeiterEntwicklungsteamSpeichern()
studiengang_kz_old = document.getElementById('mitarbeiter-entwicklungsteam-detail-textbox-studiengang').value;
beginn = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-beginn').value;
ende = document.getElementById('mitarbeiter-entwicklungsteam-detail-datum-ende').value;
entwicklungsteam_id = document.getElementById('mitarbeiter-entwicklungsteam-detail-entwicklungsteam_id').value;
if(studiengang_kz=='')
{
+8
View File
@@ -3555,6 +3555,14 @@ function StudentZeugnisDokumentArchivieren()
case 'microcredential_2':
case 'microcredential_3':
case 'microcredential_4':
case 'microdegree_1':
case 'microdegree_2':
case 'microdegree_3':
case 'microdegree_4':
case 'microdegreeabschluss_1':
case 'microdegreeabschluss_2':
case 'microdegreeabschluss_3':
case 'microdegreeabschluss_4':
xml = 'microcredential.xml.php';
break;
+3 -1
View File
@@ -364,9 +364,10 @@ class entwicklungsteam extends basis_db
$bismeldung_jahr = $datetime->format('Y');
//laden des Datensatzes
$qry = "SELECT *
$qry = "SELECT tbl_entwicklungsteam.*, tbl_besqual.*, tbl_studiengang.studiengang_kz, tbl_studiengang.melderelevant
FROM bis.tbl_entwicklungsteam
JOIN bis.tbl_besqual USING(besqualcode)
JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE mitarbeiter_uid=".$this->db_add_param($mitarbeiter_uid)."
AND (beginn is NULL OR beginn <= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 12, 31))
AND (ende is NULL OR ende >= make_date(". $this->db_add_param($bismeldung_jahr). "::INTEGER, 1, 1))";
@@ -394,6 +395,7 @@ class entwicklungsteam extends basis_db
$obj->insertvon = $row->insertvon;
$obj->ext_id = $row->ext_id;
$obj->besqual = $row->besqualbez;
$obj->melderelevant = $this->db_parse_bool($row->melderelevant);
$this->result[] = $obj;
}
+49 -1
View File
@@ -25,6 +25,7 @@
*/
require_once(dirname(__FILE__).'/basis_db.class.php');
require_once(dirname(__FILE__).'/'.EXT_FKT_PATH.'/generateZahlungsreferenz.inc.php');
require_once(dirname(__FILE__).'/variable.class.php');
class konto extends basis_db
{
@@ -432,6 +433,8 @@ class konto extends basis_db
$qry.=" ORDER BY beschreibung";
$oehBeitrag = $this->_getOEHBeitrag();
if($this->db_query($qry))
{
while($row = $this->db_fetch_object())
@@ -440,7 +443,15 @@ class konto extends basis_db
$typ->buchungstyp_kurzbz = $row->buchungstyp_kurzbz;
$typ->beschreibung = $row->beschreibung;
$typ->standardbetrag = $row->standardbetrag;
if (strtolower($typ->buchungstyp_kurzbz) === 'oeh' && $oehBeitrag)
{
$typ->standardbetrag = $oehBeitrag;
}
else
{
$typ->standardbetrag = $row->standardbetrag;
}
$typ->standardtext = $row->standardtext;
$typ->credit_points = $row->credit_points;
$typ->aktiv = $this->db_parse_bool($row->aktiv);
@@ -990,6 +1001,43 @@ class konto extends basis_db
return false;
}
}
private function _getOEHBeitrag()
{
if(!is_user_logged_in())
{
return false;
}
$variablen_obj = new variable();
$variablen_obj->loadVariables(get_uid());
$qry = "WITH semstart AS (
SELECT start FROM public.tbl_studiensemester
WHERE studiensemester_kurzbz = '". $this->db_escape($variablen_obj->variable->semester_aktuell) . "'
)
SELECT * FROM bis.tbl_oehbeitrag oehb
JOIN public.tbl_studiensemester semvon ON oehb.von_studiensemester_kurzbz = semvon.studiensemester_kurzbz
LEFT JOIN public.tbl_studiensemester sembis ON oehb.bis_studiensemester_kurzbz = sembis.studiensemester_kurzbz
JOIN semstart ON semstart.start::date >= semvon.start::date AND (sembis.studiensemester_kurzbz IS NULL OR semstart.start::date <= sembis.start::date)
ORDER BY semvon.start
LIMIT 1";
if ($this->db_query($qry))
{
if($row = $this->db_fetch_object())
{
$summe = ($row->studierendenbeitrag + $row->versicherung) * -1;
return number_format((float)$summe, 2, '.', '');
}
return false;
}
else
{
$this->errormsg = 'Fehler bei der Abfrage aufgetreten';
return false;
}
}
}
?>
+4 -2
View File
@@ -584,8 +584,9 @@ class lehreinheitmitarbeiter extends basis_db
$qry = '
WITH semester_sws_tbl AS (
SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode
SELECT
DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode, stg.melderelevant
FROM lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
@@ -598,6 +599,7 @@ class lehreinheitmitarbeiter extends basis_db
AND ss.studiensemester_kurzbz IN ('.$this->implode4SQL($studiensemester_kurzbz_arr).')
-- nur lehre, die bisgemeldet wird
AND lema.bismelden
AND stg.melderelevant
-- keine lehreinheiten ohne semesterstunden
AND lema.semesterstunden != 0
)
-26
View File
@@ -584,32 +584,6 @@ class pruefling extends basis_db
$qry .= " LIMIT 1";
if($result = $this->db_query($qry))
{
if ($this->db_num_rows($result) == 0)
return false;
else
return true;
}
else
{
$this->errormsg = 'Fehler bei einer Abfrage';
return false;
}
}
public function personAlreadyInRT($person_id, $reihungstest_id, $prestudent_id)
{
$qry = "SELECT tbl_prestudent.prestudent_id
FROM public.tbl_rt_person
JOIN public.tbl_prestudent ON tbl_prestudent.person_id = tbl_rt_person.person_id
JOIN public.tbl_prestudentstatus ON tbl_prestudent.prestudent_id = tbl_prestudentstatus.prestudent_id AND status_kurzbz = 'Bewerber'
AND tbl_prestudentstatus.studienplan_id = tbl_rt_person.studienplan_id
WHERE tbl_rt_person.person_id = " . $this->db_add_param($person_id) . "
AND tbl_rt_person.rt_id = " . $this->db_add_param($reihungstest_id) . "
AND tbl_prestudent.prestudent_id != " . $this->db_add_param($prestudent_id) . "
AND get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) = 'Bewerber'
LIMIT 1";
if($result = $this->db_query($qry))
{
if ($this->db_num_rows($result) == 0)
-3
View File
@@ -17,7 +17,6 @@ $this->phrasen['testtool/basic']='Basic';
$this->phrasen['testtool/basisgebiete']='Basisgebiete';
$this->phrasen['testtool/semester']='Semester';
$this->phrasen['testtool/reihungstestNichtFreigeschalten']='Der zuteilte Reihungstest ist noch nicht freigeschaltet';
$this->phrasen['testtool/reihungstestNichtRegistriert']='Sie sind für den Reihungstest nicht registriert';
$this->phrasen['testtool/reihungstestKannNichtGeladenWerden']='Der Reihungstest dem Sie zugeteilt sind, kann nicht geladen werden. Melden Sie sich bitte bei der Reihungstestaufsicht.';
$this->phrasen['testtool/geburtsdatumStimmtNichtUeberein']='Ihr Geburtsdatum stimmt nicht mit unseren Daten überein. Bitte wenden Sie sich an die Aufsichtsperson';
$this->phrasen['testtool/home']='Home';
@@ -36,8 +35,6 @@ $this->phrasen['testtool/spracheDerTestfragen']='Gewünschte Sprache der Testfra
$this->phrasen['testtool/einleitung']='Einleitung';
$this->phrasen['testtool/blaettern']='Blättern';
$this->phrasen['testtool/demo']='Demobeispiel ansehen';
$this->phrasen['testtool/abbrechen']='Abbrechen';
$this->phrasen['testtool/startGebiet']='Gebiet starten';
$this->phrasen['testtool/okKlickenUmZuStarten']='Klicken Sie OK um dieses Gebiet zu starten. \nSie haben für die Bearbeitung ein Zeitlimit von';
$this->phrasen['testtool/bitteZuerstAnmelden']='Bitte zuerst anmelden!';
$this->phrasen['testtool/fehlerBeimGenerierenDesFragenpools']='Fehler beim generieren des Fragenpools';
-3
View File
@@ -17,7 +17,6 @@ $this->phrasen['testtool/basic']='Basic';
$this->phrasen['testtool/basisgebiete']='Basic test';
$this->phrasen['testtool/semester']='Semester';
$this->phrasen['testtool/reihungstestNichtFreigeschalten']='The entrance examination assigned has not yet been activated.';
$this->phrasen['testtool/reihungstestNichtRegistriert']='You are not registered for the placement test.';
$this->phrasen['testtool/reihungstestKannNichtGeladenWerden']='The placement test you are assigned to could not be loaded. Please contact the placement test supervisior.';
$this->phrasen['testtool/geburtsdatumStimmtNichtUeberein']='Your date of birth does not correspond to the data we have. Please speak to the supervisor. ';
$this->phrasen['testtool/home']='Home';
@@ -36,8 +35,6 @@ $this->phrasen['testtool/spracheDerTestfragen']='Desired language of questions';
$this->phrasen['testtool/einleitung']='Introduction';
$this->phrasen['testtool/blaettern']='Browse';
$this->phrasen['testtool/demo']='See an example';
$this->phrasen['testtool/abbrechen']='Cancel';
$this->phrasen['testtool/startGebiet']='Start the section';
$this->phrasen['testtool/okKlickenUmZuStarten']='Click OK to start this section. \nYou have a timelimit of';
$this->phrasen['testtool/bitteZuerstAnmelden']='Please log in first!';
$this->phrasen['testtool/fehlerBeimGenerierenDesFragenpools']='Error in generating the pool of questions.';
+1 -19
View File
@@ -407,7 +407,6 @@ html {
background-color: var(--fhc-background);
border-color: var(--fhc-border);
padding: var(--fhc-cis-main-py) var(--fhc-cis-main-px);
min-width: 0; /* fix flex-grow with tabulator exceeding width */
}
#cis-main .fa-arrow-up-right-from-square {
@@ -857,25 +856,8 @@ html {
}
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable::after {
content: "▾";
position: absolute;
right: 6px;
color: rgba(176, 176, 106, 0.73);;
font-size: x-large;
bottom: 6px;
pointer-events: none;
}
.bordered-modal {
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.5rem;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}
}
-6
View File
@@ -38,9 +38,3 @@ textarea[name="anmerkung"] {
{
border-color: black;
}
.node-verband {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
+4 -4
View File
@@ -197,10 +197,6 @@ html.fs_huge {
margin-bottom: -1px;
}
.tiny-90 div.tox.tox-tinymce {
height: 90% !important;
}
/* slim begin */
.stv .form-label {
margin-bottom: .15rem;
@@ -281,3 +277,7 @@ html.fs_huge {
}
*/
/* slim ende */
.fhc-xxl-modal {
min-width: 80vw;
}
+91 -1
View File
@@ -305,4 +305,94 @@
/* If you use hover rows, you need to ensure the sticky cell matches the hover color */
#abgabetable .tabulator-row:hover .tabulator-cell.sticky-col {
background-color: #ccc; /* Match your existing hover color */
}
}
.tabulator-cell {
container-type: inline-size;
}
.tabulator-col-title {
container-type: inline-size;
}
@container (max-width: 100px) {
.full-text {
display: none !important;
}
.short-text {
display: inline-block !important;
width: 100%;
}
}
/*conditional tooltips fix*/
.p-tooltip.custom-tooltip {
z-index: 8001 !important;
}
/* Shrinks font and table rows for desktop users who have zoomed in their browser (150%+).
Does not affect mobile/touchscreen devices, which use touch input instead of a mouse. */
@media (pointer: fine) and (min-resolution: 1.5dppx) {
html.abgabetool {
font-size: 0.5rem;
}
.abgabetool .tabulator-cell,
.abgabetool .tabulator-row {
height: 20px;
max-height: 20px;
}
}
/*confetti celebration on endupload - impossible to miss*/
#confetti-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 9999;
overflow: hidden;
}
.confetti-piece {
position: absolute;
opacity: 0;
will-change: top, transform, opacity;
}
/* Background Rain */
@keyframes fallAndSpin {
0% {
top: var(--start-y);
transform: translate3d(0, 0, 0) rotateX(0deg) rotateY(0deg);
opacity: 1;
}
100% {
top: 105vh;
transform: translate3d(var(--drift), 0, 0) rotateX(720deg) rotateY(360deg);
opacity: 0.3;
}
}
/* Corner Cannons*/
@keyframes cannonBlast {
0% {
transform: translate3d(0, 0, 0) scale(0.3) rotate(0deg);
opacity: 1;
animation-timing-function: cubic-bezier(0.1, 0.8, 0.2, 1);
}
30% {
transform: translate3d(var(--blast-x), var(--blast-y), 0) scale(1.2) rotate(270deg);
opacity: 1;
animation-timing-function: linear;
}
100% {
transform: translate3d(calc(var(--blast-x) * 1.4), 15vh, 0) scale(0.4) rotate(630deg);
opacity: 0;
}
}
+22
View File
@@ -77,6 +77,13 @@ export default {
}
};
},
patchProjektarbeitAbgabeMultiple(payload) {
return {
method: 'post',
url: '/api/frontend/v1/Abgabe/patchProjektarbeitAbgabeMultiple',
params: payload
};
},
deleteProjektarbeitAbgabe(paabgabe_id) {
return {
method: 'post',
@@ -84,6 +91,13 @@ export default {
params: { paabgabe_id }
};
},
deleteProjektarbeitAbgabeMultiple(paabgabe_ids) {
return {
method: 'post',
url: '/api/frontend/v1/Abgabe/deleteProjektarbeitAbgabeMultiple',
params: { paabgabe_ids }
};
},
postSerientermin(datum, paabgabetyp_kurzbz, bezeichnung, kurzbz, upload_allowed, projektarbeit_ids, fixtermin) {
return {
method: 'post',
@@ -139,6 +153,14 @@ export default {
url: '/api/frontend/v1/Abgabe/getSignaturStatusForProjektarbeitAbgaben',
params: {paabgabe_ids, student_uid},
};
},
postStudentProjektarbeitTitel(projektarbeit_id, titel) {
return {
method: 'post',
url: '/api/frontend/v1/Abgabe/postStudentProjektarbeitTitel',
params: {projektarbeit_id, titel},
};
}
};
-14
View File
@@ -35,19 +35,5 @@ export default {
method: 'get',
url: `/api/frontend/v1/Lehre/Pruefungen/${lehrveranstaltung_id}`
};
},
getZugewieseneLv(uid, sem_kurzbz){
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getZugewieseneLv',
params: { uid, sem_kurzbz}
};
},
getLeForLv(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getLeForLv',
params: { lv_id, sem_kurzbz }
};
}
};
+8 -5
View File
@@ -17,13 +17,16 @@
export default {
getMessages(params) {
let url = 'api/frontend/v1/messages/messages/getMessages'
+ '/' + params.id
+ '/' + params.type;
if(params.size && params.page) {
url += '/' + params.size
+ '/' + params.page;
}
return {
method: 'get',
url: 'api/frontend/v1/messages/messages/getMessages/'
+ params.id + '/'
+ params.type + '/'
+ params.size + '/'
+ params.page
url: url
};
},
getVorlagen(){
-74
View File
@@ -1,74 +0,0 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getStudentenNoten(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getStudentenNoten',
params: { lv_id, sem_kurzbz }
};
},
getNoten(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getNoten'
};
},
saveStudentenNoten(password, noten, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentenNoten',
params: { password, noten, lv_id, sem_kurzbz }
};
},
saveNotenvorschlag(lv_id, sem_kurzbz, student_uid, note) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlag',
params: { lv_id, sem_kurzbz, student_uid, note }
};
},
saveStudentPruefung(student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ){
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentPruefung',
params: { student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ }
};
},
createPruefungen(uids, datum, lva_id, sem_kurzbz){
return {
method: 'post',
url: '/api/frontend/v1/Noten/createPruefungen',
params: { uids, datum, lva_id, sem_kurzbz }
};
},
saveNotenvorschlagBulk(lv_id, sem_kurzbz, noten) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlagBulk',
params: { lv_id, sem_kurzbz, noten }
};
},
saveStudentPruefungBulk(lv_id, sem_kurzbz, pruefungen) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/savePruefungenBulk',
params: { lv_id, sem_kurzbz, pruefungen }
};
}
};
-6
View File
@@ -16,12 +16,6 @@
*/
export default {
getStudiensemester() {
return {
method: 'get',
url: '/api/frontend/v1/Studiensemester/getStudiensemester'
};
},
getAllStudiensemesterAndAktOrNext() {
return {
method: 'get',
+6 -10
View File
@@ -16,20 +16,16 @@
*/
export default {
getCourselist(student_uid, start_date, end_date, stundenplan) {
getCourselist(params) {
return {
method: 'get',
url: 'api/frontend/v1/stv/LvTermine/getStundenplan/' + encodeURIComponent(student_uid) + '/'
+ encodeURIComponent(start_date) + '/'
+ encodeURIComponent(end_date) + '/'
+ encodeURIComponent(stundenplan) + '/'
+ encodeURIComponent(true)
url: 'api/frontend/v1/stv/LvTermine/getStundenplan/' + params.student_uid + '/'
+ params.start_date + '/'
+ params.end_date + '/'
+ params.group_consecutiveHours + '/'
+ params.dbStundenplanTable
};
},
exportCalendar(student_uid, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?student_uid=' + encodeURIComponent(student_uid) + '&db_stpl_table='+encodeURIComponent(stundenplan);
},
getStudiensemester(){
return {
method: 'get',
+15 -3
View File
@@ -38,6 +38,10 @@ export default {
};
},
insert(params) {
if(params.betrag)
{
params.betrag = params.betrag.replace(',', '.');
}
return {
method: 'post',
url: 'api/frontend/v1/stv/konto/insert',
@@ -52,6 +56,10 @@ export default {
};
},
edit(params) {
if(params.betrag)
{
params.betrag = params.betrag.replace(',', '.');
}
return {
method: 'post',
url: 'api/frontend/v1/stv/konto/update',
@@ -65,10 +73,14 @@ export default {
params: { buchungsnr }
};
},
getBuchungstypen() {
getBuchungstypen(studiensemester_kurzbz) {
let url = 'api/frontend/v1/stv/konto/getBuchungstypen'
if (!!studiensemester_kurzbz)
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
return {
method: 'get',
url: 'api/frontend/v1/stv/konto/getBuchungstypen'
url: url
};
}
},
};
-15
View File
@@ -1,15 +0,0 @@
export default {
get() {
return {
method: 'get',
url: '/api/frontend/v1/lv/config/get'
};
},
set(params) {
return {
method: 'post',
url: 'api/frontend/v1/lv/config/set',
params
};
}
};
@@ -18,14 +18,6 @@ export default {
params: newData
};
},
getGruppe(gruppe)
{
return {
method: 'post',
url: '/api/frontend/v1/lv/gruppe/getGruppe/',
params: gruppe
};
},
getByLehreinheit(lehreinheit_id)
{
return {
@@ -1,19 +0,0 @@
export default {
getCourselist(le_id, start_date, end_date, stundenplan)
{
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/getLeEvents/' + encodeURIComponent(le_id) + "/" + encodeURIComponent(start_date) + "/" + encodeURIComponent(end_date) + "/" + encodeURIComponent(stundenplan),
};
},
exportCalendar(le_id, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?lehreinheit_id=' + encodeURIComponent(le_id) + '&db_stpl_table='+encodeURIComponent(stundenplan);
},
getStudiensemester(){
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getStudiensemester/'
};
},
};
@@ -1,19 +0,0 @@
export default {
getCourselist(lv_id, start_date, end_date, stundenplan)
{
return {
method: 'get',
url: '/api/frontend/v1/lvPlan/getLvEvents/' + encodeURIComponent(lv_id) + "/" + encodeURIComponent(start_date) + "/" + encodeURIComponent(end_date) + "/" + encodeURIComponent(stundenplan),
};
},
exportCalendar(lv_id, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?lehrveranstaltung_id=' + encodeURIComponent(lv_id) + '&db_stpl_table='+encodeURIComponent(stundenplan);
},
getStudiensemester(){
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getStudiensemester/'
};
},
};
-25
View File
@@ -1,25 +0,0 @@
import Grades from '../factory/stv/grades.js';
export default {
...Grades,
getCertificate(lv_id, studiensemester_kurzbz) {
let url = 'api/frontend/v1/lv/noten/getCertificate/' + encodeURIComponent(lv_id);
if (!!studiensemester_kurzbz) {
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
}
return {
method: 'get',
url: url
};
},
getTeacherProposal(lv_id, studiensemester_kurzbz) {
let url = 'api/frontend/v1/lv/noten/getTeacherProposal/' + encodeURIComponent(lv_id);
if (!!studiensemester_kurzbz) {
url = url + '/' + encodeURIComponent(studiensemester_kurzbz);
}
return {
method: 'get',
url: url
};
},
};
+2 -9
View File
@@ -1,16 +1,9 @@
export default {
getLETabs()
getTabs()
{
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getLETabs/'
};
},
getLVTabs()
{
return {
method: 'get',
url: '/api/frontend/v1/lv/setup/getLVTabs/'
url: '/api/frontend/v1/lv/setup/getTabs/'
};
},
}
+4 -7
View File
@@ -25,9 +25,6 @@ const app = Vue.createApp({
},
computed: {
viewData() {
return { uid: this.uid}
},
student_uid_computed() {
return this.student_uid ?? this.uid
},
@@ -55,10 +52,10 @@ const app = Vue.createApp({
},
template: `
<template v-if="comp && uid">
<AbgabetoolStudent v-if="comp == 'AbgabetoolStudent'" :viewData="viewData" :student_uid_prop="student_uid_computed"></AbgabetoolStudent>
<AbgabetoolMitarbeiter v-if="comp == 'AbgabetoolMitarbeiter'" :viewData="viewData"></AbgabetoolMitarbeiter>
<AbgabetoolAssistenz v-if="comp == 'AbgabetoolAssistenz'" :viewData="viewData" :stg_kz_prop="stg_kz_computed"></AbgabetoolAssistenz>
<DeadlineOverview v-if="comp == 'DeadlinesOverview'" :viewData="viewData"></DeadlineOverview>
<AbgabetoolStudent v-if="comp == 'AbgabetoolStudent'" :student_uid_prop="student_uid_computed"></AbgabetoolStudent>
<AbgabetoolMitarbeiter v-if="comp == 'AbgabetoolMitarbeiter'"></AbgabetoolMitarbeiter>
<AbgabetoolAssistenz v-if="comp == 'AbgabetoolAssistenz'" :stg_kz_prop="stg_kz_computed"></AbgabetoolAssistenz>
<DeadlineOverview v-if="comp == 'DeadlinesOverview'"></DeadlineOverview>
</template>
`
});
-7
View File
@@ -20,7 +20,6 @@ import Studium from "../../components/Cis/Studium/Studium.js";
import ApiRenderers from '../../api/factory/renderers.js';
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import Benotungstool from "../../components/Cis/Benotungstool/Benotungstool.js";
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -76,12 +75,6 @@ const router = VueRouter.createRouter({
component: Raumsuche,
props: true
},
{
path: `/Cis/Benotungstool/:lv_id/:sem_kurzbz`,
name: 'Benotungstool',
component: Benotungstool,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
+31 -2
View File
@@ -4,7 +4,9 @@ export default {
name: 'BootstrapModal',
data: () => ({
modal: null,
fullscreen: false
fullscreen: false,
expandBtnHovered: false,
expandBtnFocused: false,
}),
props: {
backdrop: {
@@ -70,6 +72,29 @@ export default {
this.$emit('toggleFullscreen')
}
},
computed: {
getExpandButtonStyles() {
const hovered = this.expandBtnHovered;
const focused = this.expandBtnFocused;
return `display: flex;
align-items: center;
justify-content: center;
width: 1em;
height: 1em;
padding: 0;
border: 0;
background: transparent;
font-size: 1em;
opacity: 0.5;
color: inherit;
cursor: pointer;
line-height: 1;
transition: opacity 0.15s ease;
opacity: ${focused ? '1' : hovered ? '0.75' : '0.5'};
outline: ${focused ? '1px solid currentColor' : 'none'};
outline-offset: 2px;`
}
},
mounted() {
if (this.$refs.modal)
this.modal = new bootstrap.Modal(this.$refs.modal, {
@@ -140,9 +165,13 @@ export default {
<div class="d-flex align-items-center ms-auto gap-2">
<button
type="button"
class="btn mb-1"
:style="getExpandButtonStyles"
v-if="allowFullscreenExpand"
@click="toggleFullscreen"
@mouseenter="expandBtnHovered = true"
@mouseleave="expandBtnHovered = false"
@focusin="expandBtnFocused = true"
@focusout="expandBtnFocused = false"
:aria-label="fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'"
>
<i v-if="!fullscreen" class="fa-solid fa-expand"></i>
@@ -2,6 +2,7 @@ import BsModal from '../../Bootstrap/Modal.js';
import VueDatePicker from '../../vueDatepicker.js.php';
import ApiAbgabe from '../../../api/factory/abgabe.js'
import { getDateStyleClass } from "./getDateStyleClass.js";
import { compareISODateValues, formatISODate, getViennaTodayISO } from "./dateUtils.js";
export const AbgabeMitarbeiterDetail = {
name: "AbgabeMitarbeiterDetail",
@@ -46,12 +47,7 @@ export const AbgabeMitarbeiterDetail = {
eidAkzeptiert: false,
enduploadTermin: null,
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
speedDialItems: [{
label: Vue.computed(() => this.$p.t('abgabetool/c4newAbgabetermin')),
icon: "fa fa-plus",
command: this.openCreateNewAbgabeModal,
disabled: Vue.computed(() => !this.getAllowedToCreateNewTermin)
},
speedDialItems: [
{
label: Vue.computed(() => this.$p.t('abgabetool/c4benoten')),
icon: "fa fa-user-check",
@@ -81,6 +77,9 @@ export const AbgabeMitarbeiterDetail = {
}
},
methods: {
terminIsInvalid(termin) {
return termin.note?.positiv == false && !termin.beurteilungsnotiz
},
getNoteBezeichnung(termin){
if(termin.noteBackend?.bezeichnung) {
return termin.noteBackend?.positiv ? this.$capitalize(this.$p.t('abgabetool/c4positivBenotet')) + ' ✅' : this.$capitalize(this.$p.t('abgabetool/c4negativBenotet')) + ' ❌'
@@ -113,6 +112,8 @@ export const AbgabeMitarbeiterDetail = {
if(newTerminRes.note) {
newTerminRes.note = noteOpt
newTerminRes.noteBackend = noteOpt // certain UI elements should only reflect persisted state
termin.allowedToDelete = false
newTerminRes.allowedToDelete = false
}
newTerminRes.invertedFixtermin = !newTerminRes.fixtermin
const existingTerminRes = res.data[1]
@@ -132,13 +133,13 @@ export const AbgabeMitarbeiterDetail = {
} else {
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
existingTerminRes.note = noteOptExisting
termin.paabgabetyp_kurzbz = newTerminRes.paabgabetyp_kurzbz
termin.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour"
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
}
this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum))
this.projektarbeit.abgabetermine.sort((a, b) => compareISODateValues(a.datum, b.datum))
const index = this.projektarbeit.abgabetermine.findIndex(t => termin.paabgabe_id == t.paabgabe_id)
@@ -159,7 +160,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '', // todo kurzbz textfield value vorschlag für qualgates
'datum': new Date().toISOString().split('T')[0],
'datum': getViennaTodayISO(),
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': false,
@@ -337,16 +338,7 @@ export const AbgabeMitarbeiterDetail = {
}
},
formatDate(dateParam) {
// unsafe for datepickers, dont use there
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`
return formatISODate(dateParam)
},
openCreateNewAbgabeModal() {
if(this.projektarbeit?.betreuerart_kurzbz == 'Zweitbegutachter') {
@@ -364,7 +356,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'datum': getViennaTodayISO(),
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': typ.upload_allowed_default,
@@ -398,7 +390,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'datum': getViennaTodayISO(),
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': false,
@@ -446,7 +438,7 @@ export const AbgabeMitarbeiterDetail = {
}
},
getMessagePtStyle() {
// adjust outer spacing and internal padding to appear similar to doenload button in size
// adjust outer spacing and internal padding to appear similar to download button in size
return {
root: {
style: {
@@ -561,6 +553,12 @@ export const AbgabeMitarbeiterDetail = {
class: "custom-tooltip"
}
},
getTooltipBeurteilungsnotiz() {
return {
value: this.$p.t('abgabetool/c4beurteilungsnotizBeiNegNote'),
class: "custom-tooltip"
}
},
getProjektarbeitTitel() {
if(this.projektarbeit?.titel) return this.$capitalize(this.$p.t('abgabetool/c4titel')) + ': ' + this.projektarbeit.titel
@@ -591,7 +589,7 @@ export const AbgabeMitarbeiterDetail = {
'fixtermin': false,
'invertedFixtermin': true,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'datum': getViennaTodayISO(),
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': typ.upload_allowed_default,
@@ -627,6 +625,7 @@ export const AbgabeMitarbeiterDetail = {
dialogClass="bordered-modal modal-lg"
:backdrop="true"
@hideBsModal="showAutomagicModalPhrase=false;"
bodyClass="px-4 py-4"
>
<template v-slot:title>
<div>
@@ -656,6 +655,7 @@ export const AbgabeMitarbeiterDetail = {
:enable-time-picker="false"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
:text-input="true"
auto-apply>
</VueDatePicker>
@@ -806,6 +806,7 @@ export const AbgabeMitarbeiterDetail = {
:enable-time-picker="false"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
:text-input="true"
auto-apply>
</VueDatePicker>
@@ -816,7 +817,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="col-12 col-md-9">
<Dropdown
:style="{'width': '100%'}"
:disabled="!termin.allowedToSave"
:disabled="!termin.allowedToSave || termin.abgabedatum !== null || termin.noteBackend"
:placeholder="getPlaceholderTermin(termin)"
v-model="termin.bezeichnung"
@change="handleChangeAbgabetyp(termin)"
@@ -851,8 +852,10 @@ export const AbgabeMitarbeiterDetail = {
</div>
<div class="row mt-2" v-if="termin.bezeichnung?.benotbar">
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4notizQualGatev2') )}}</div>
<div class="col-12 col-md-9">
<Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz" rows="1" class="w-100" :disabled="!termin.allowedToSave"></Textarea>
<div class="col-12 col-md-9" v-tooltip.right="terminIsInvalid(termin) && getTooltipBeurteilungsnotiz">
<Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz"
:class="{ 'p-invalid': terminIsInvalid(termin) }"
rows="1" class="w-100" :disabled="!termin.allowedToSave"></Textarea>
</div>
</div>
@@ -874,6 +877,7 @@ export const AbgabeMitarbeiterDetail = {
:disabled="true"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
>
</VueDatePicker>
</div>
@@ -889,9 +893,6 @@ export const AbgabeMitarbeiterDetail = {
<Message v-else-if="termin?.signatur == false" severity="error" :closable="false" :pt="getMessagePtStyle"> {{ $capitalize($p.t('abgabetool/c4keineSignatur')) }} </Message>
<Message v-else-if="termin?.signatur == 'error'" severity="warn" :closable="false" :pt="getMessagePtStyle"> {{ $capitalize($p.t('abgabetool/c4signaturServerError')) }} </Message>
</div>
<!-- <div v-else class="col-auto">-->
<!-- <Message severity="info" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4noFileFound') }} </Message>-->
<!-- </div>-->
</div>
</template>
@@ -907,7 +908,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="col-12 col-md-9">
<div class="row">
<div class="col-auto">
<button v-if="termin.allowedToSave" style="max-height: 40px;" class="btn btn-primary border-0" @click="saveTermin(termin)">
<button v-if="termin.allowedToSave && !terminIsInvalid(termin)" style="max-height: 40px;" class="btn btn-primary border-0" @click="saveTermin(termin)">
{{ $capitalize( $p.t('abgabetool/c4save') )}}
<i class="fa-solid fa-floppy-disk"></i>
</button>
@@ -944,7 +945,8 @@ export const AbgabeMitarbeiterDetail = {
<bs-modal
ref="modalContainerZusatzdaten"
class="bootstrap-prompt"
dialogClass="bordered-modal modal-lg">
dialogClass="bordered-modal modal-lg"
bodyClass="px-4 py-4">
<template v-slot:title>
<div>
{{$capitalize( $p.t('abgabetool/c4enduploadZusatzdaten') )}}
@@ -3,6 +3,9 @@ import BsModal from '../../Bootstrap/Modal.js';
import VueDatePicker from '../../vueDatepicker.js.php';
import ApiAbgabe from '../../../api/factory/abgabe.js'
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { formatISODate, getViennaTodayISO } from "./dateUtils.js";
import { validateThesisTitle } from './titleValidation.js'
export const AbgabeStudentDetail = {
name: "AbgabeStudentDetail",
@@ -20,7 +23,16 @@ export const AbgabeStudentDetail = {
VueDatePicker,
FhcOverlay
},
inject: ['notenOptions', 'isMobile', 'isViewMode', 'moodle_link'],
inject: [
'notenOptions',
'isMobile',
'isViewMode',
'moodle_link',
'confetti_on_endupload',
'title_edit_allowed',
'siginfolink_german',
'siginfolink_english'
],
props: {
projektarbeit: {
type: Object,
@@ -31,12 +43,14 @@ export const AbgabeStudentDetail = {
default: false
}
},
emits: ['titel-updated'],
data() {
return {
loading: false,
eidAkzeptiert: false,
enduploadTermin: null,
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
editingTitel: '',
form: Vue.reactive({
sprache: '',
abstract: '',
@@ -49,9 +63,120 @@ export const AbgabeStudentDetail = {
}
},
methods: {
confettiCannons() {
const container = document.getElementById('confetti-container');
if (!container) return;
const colors = ['#FFC107', '#FF5722', '#E91E63', '#00BCD4', '#4CAF50', '#9C27B0'];
const shapes = ['50%', '0%'];
const fragment = document.createDocumentFragment();
// Corner Cannons - Slowed Down)
const cannonCount = 150;
for (let i = 0; i < cannonCount; i++) {
const leftConfetti = document.createElement('div');
leftConfetti.classList.add('confetti-piece');
leftConfetti.style.left = '0px';
leftConfetti.style.top = '100%';
const rightConfetti = document.createElement('div');
rightConfetti.classList.add('confetti-piece');
rightConfetti.style.left = '100vw';
rightConfetti.style.top = '100%';
const colorL = colors[Math.floor(Math.random() * colors.length)];
const colorR = colors[Math.floor(Math.random() * colors.length)];
const shapeL = shapes[Math.floor(Math.random() * shapes.length)];
const shapeR = shapes[Math.floor(Math.random() * shapes.length)];
// Left Styles
leftConfetti.style.background = colorL;
leftConfetti.style.borderRadius = shapeL;
leftConfetti.style.width = `${Math.random() * 10 + 6}px`;
leftConfetti.style.height = `${Math.random() * 14 + 6}px`;
leftConfetti.style.setProperty('--blast-x', `${Math.random() * 50 + 10}vw`);
leftConfetti.style.setProperty('--blast-y', `-${Math.random() * 65 + 30}vh`);
// Right Styles
rightConfetti.style.background = colorR;
rightConfetti.style.borderRadius = shapeR;
rightConfetti.style.width = `${Math.random() * 10 + 6}px`;
rightConfetti.style.height = `${Math.random() * 14 + 6}px`;
rightConfetti.style.setProperty('--blast-x', `-${Math.random() * 50 + 10}vw`);
rightConfetti.style.setProperty('--blast-y', `-${Math.random() * 65 + 30}vh`);
// Increased durations to 3s - 5s for a floating gravity effect
const durationL = Math.random() * 2 + 3;
const durationR = Math.random() * 2 + 3;
const delayL = Math.random() * 0.2;
const delayR = Math.random() * 0.2;
leftConfetti.style.animation = `cannonBlast ${durationL}s linear ${delayL}s both`;
rightConfetti.style.animation = `cannonBlast ${durationR}s linear ${delayR}s both`;
fragment.appendChild(leftConfetti);
fragment.appendChild(rightConfetti);
setTimeout(() => leftConfetti.remove(), (delayL + durationL) * 1000);
setTimeout(() => rightConfetti.remove(), (delayR + durationR) * 1000);
}
container.appendChild(fragment);
},
openTitelEdit() {
this.editingTitel = this.projektarbeit.titel ?? '';
this.$refs.modalTitelEdit.show();
},
async saveTitel() {
const validation = validateThesisTitle(this.editingTitel);
if (!validation.isValid) {
if (validation.error === 'empty') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4emptyThesisTitle'))
} else if (validation.error === 'invalid_characters') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4invalidCharactersThesisTitle'))
}
return false;
}
const confirmed = await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4confirmTitelSpeichern'),
acceptLabel: this.$capitalize(this.$p.t('ui/speichern')),
acceptClass: 'p-button-primary',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'p-button-secondary'
});
if (confirmed === false) return;
this.loading = true;
this.$api.call(
ApiAbgabe.postStudentProjektarbeitTitel(
this.projektarbeit.projektarbeit_id,
validation.cleanedTitle
)
).then(res => {
if (res.meta.status === 'success') {
this.projektarbeit.titel = res.data;
this.$emit('titel-updated', {
projektarbeit_id: this.projektarbeit.projektarbeit_id,
titel: res.data
});
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
this.$refs.modalTitelEdit.hide();
} else {
this.$fhcAlert.alertError(this.$capitalize(this.$p.t('abgabetool/c4titelSaveError')));
}
}).finally(() => {
this.loading = false;
});
},
getNoteBezeichnung(termin){
const noteOpt = this.notenOptions.find(opt => opt.note == termin.note)
if(noteOpt?.bezeichnung) {
return noteOpt?.positiv ? this.$capitalize(this.$p.t('abgabetool/c4positivBenotet')) + ' ✅' : this.$capitalize(this.$p.t('abgabetool/c4negativBenotet')) + ' ❌'
} else if(noteOpt?.benotbar === true && !termin.note) {
@@ -65,7 +190,7 @@ export const AbgabeStudentDetail = {
this.$fhcAlert.alertWarning(this.$capitalize(this.$p.t('global/warningChooseFile')));
return false
}
if(endupload) {
if(await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/confirmEnduploadSpeichern'),
@@ -77,16 +202,16 @@ export const AbgabeStudentDetail = {
return false
}
}
return true;
},
async triggerEndupload() {
if (!await this.validate(this.enduploadTermin, true))
{
return false;
}
// post endabgabe
const formData = new FormData();
formData.append('paabgabetyp_kurzbz', this.enduploadTermin.paabgabetyp_kurzbz)
@@ -94,14 +219,14 @@ export const AbgabeStudentDetail = {
formData.append('paabgabe_id', this.enduploadTermin.paabgabe_id)
formData.append('student_uid', this.projektarbeit.student_uid)
formData.append('bperson_id', this.projektarbeit.bperson_id)
formData.append('sprache', this.form['sprache'].sprache)
formData.append('abstract', this.form['abstract'])
formData.append('abstract_en', this.form['abstract_en'])
formData.append('schlagwoerter', this.form['schlagwoerter'])
formData.append('schlagwoerter_en', this.form['schlagwoerter_en'])
formData.append('seitenanzahl', this.form['seitenanzahl'])
for (let i = 0; i < this.enduploadTermin.file.length; i++) {
formData.append('file', this.enduploadTermin.file[i]);
}
@@ -109,39 +234,31 @@ export const AbgabeStudentDetail = {
this.$api.call(ApiAbgabe.postStudentProjektarbeitEndupload(formData))
.then(res => {
this.handleUploadRes(res, this.enduploadTermin)
if(this.confetti_on_endupload && res.meta.status == "success") {
this.confettiCannons()
}
}).finally(()=> {
this.loading = false
this.loading = false
})
this.$refs.modalContainerEnduploadZusatzdaten.hide()
},
downloadAbgabe(termin) {
const url = `/api/frontend/v1/Abgabe/getStudentProjektarbeitAbgabeFile?paabgabe_id=${termin.paabgabe_id}&student_uid=${this.projektarbeit.student_uid}&projektarbeit_id=${this.projektarbeit.projektarbeit_id}`;
window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url)
// this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
},
formatDate(dateParam) {
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`
return formatISODate(dateParam)
},
async upload(termin) {
// only do this on endupload
if (! await this.validate(termin))
{
return false;
}
if(termin.bezeichnung?.paabgabetyp_kurzbz === 'end') {
// open endupload form modal for further inputs
this.enduploadTermin = termin
this.$refs.modalContainerEnduploadZusatzdaten.show()
} else {
@@ -151,7 +268,7 @@ export const AbgabeStudentDetail = {
formData.append('paabgabe_id', termin.paabgabe_id)
formData.append('student_uid', this.projektarbeit.student_uid)
formData.append('bperson_id', this.projektarbeit.bperson_id)
for (let i = 0; i < termin.file.length; i++) {
formData.append('file', termin.file[i]);
}
@@ -161,7 +278,7 @@ export const AbgabeStudentDetail = {
.then(res => {
this.handleUploadRes(res, termin)
}).finally(()=> {
this.loading = false
this.loading = false
})
}
},
@@ -169,21 +286,18 @@ export const AbgabeStudentDetail = {
if(res.meta.status == "success") {
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4fileUploadSuccessv3')))
// update 'abgabedatum' for successful upload -> shows the pdf icon and date once set
termin.abgabedatum = new Date().toISOString().split('T')[0];
termin.abgabedatum = getViennaTodayISO();
if(res?.data?.signatur !== undefined) {
termin.signatur = res.data.signatur
}
} else {
this.$fhcAlert.alertError(this.$capitalize(this.$p.t('abgabetool/c4fileUploadErrorv3')))
}
if(res.meta.signaturInfo) {
this.$fhcAlert.alertInfo(res.meta.signaturInfo)
}
},
getOptionLabel(option) {
return option.sprache
@@ -195,7 +309,6 @@ export const AbgabeStudentDetail = {
},
watch: {
projektarbeit(newVal) {
// default select german if projektarbeit sprache was null
this.form.sprache = newVal.sprache ? this.allActiveLanguages.find(lang => lang.sprache == newVal.sprache) : this.allActiveLanguages.find(lang => lang.sprache == 'German')
this.form.abstract = newVal.abstract ?? ''
this.form.abstract_en = newVal.abstract_en ?? ''
@@ -203,15 +316,22 @@ export const AbgabeStudentDetail = {
this.form.schlagwoerter_en = newVal.schlagwoerter_en ?? ''
this.form.kontrollschlagwoerter = newVal.kontrollschlagwoerter ?? ''
this.form.seitenanzahl = newVal.seitenanzahl ?? 1
}
},
computed: {
getSignaturInfoLink() {
if(this.$p.user_language.value == 'German' && this.siginfolink_german) return this.siginfolink_german
else if (this.$p.user_language.value == 'English' && this.siginfolink_english) return this.siginfolink_english
},
getSignaturInfoAvailable() {
if(this.$p.user_language.value == 'German' && this.siginfolink_german) return true
else if (this.$p.user_language.value == 'English' && this.siginfolink_english) return true
else return false
},
getMoodleLink() {
return this.moodle_link + this.projektarbeit.studiengang_kz
return this.moodle_link + this.projektarbeit.studiengang_kz
},
getMessagePtStyle() {
// adjust outer spacing and internal padding to appear similar to doenload button in size
return {
root: {
style: {
@@ -244,102 +364,84 @@ export const AbgabeStudentDetail = {
})
return qgatefound
},
isTitelEditAllowed() {
return this.title_edit_allowed && !this.isViewMode && !this.projektarbeit?.note;
},
getTooltipVerspaetet() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerspaetet')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerspaetet')), class: "custom-tooltip" }
},
getTooltipVerpasst() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerpasst')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipVerpasst')), class: "custom-tooltip" }
},
getTooltipAbzugeben() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbzugeben')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbzugeben')), class: "custom-tooltip" }
},
getTooltipStandard() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipStandardv2')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipStandardv2')), class: "custom-tooltip" }
},
getTooltipAbgegeben() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgegeben')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgegeben')), class: "custom-tooltip" }
},
getTooltipFixtermin() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipFixtermin')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipFixtermin')), class: "custom-tooltip" }
},
getTooltipAbgabeDetected() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgabeDetected')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipAbgabeDetected')), class: "custom-tooltip" }
},
getTooltipNotAllowedToUpload() {
if(this.isViewMode) {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedInViewMode')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedInViewMode')), class: "custom-tooltip" }
} else {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedRegular')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4studentAbgabeNotAllowedRegular')), class: "custom-tooltip" }
}
},
getTooltipBeurteilungerforderlich() {
return {
value: this.$capitalize(this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich')),
class: "custom-tooltip"
}
return { value: this.$capitalize(this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich')), class: "custom-tooltip" }
},
getTooltipBestanden() {
return {
value: this.$p.t('abgabetool/c4tooltipBestanden'),
class: "custom-tooltip"
}
return { value: this.$p.t('abgabetool/c4tooltipBestanden'), class: "custom-tooltip" }
},
getTooltipNichtBestanden() {
return {
value: this.$p.t('abgabetool/c4tooltipNichtBestanden'),
class: "custom-tooltip"
}
return { value: this.$p.t('abgabetool/c4tooltipNichtBestanden'), class: "custom-tooltip" }
},
},
created() {
},
mounted() {
},
template: `
<FhcOverlay :active="loading"></FhcOverlay>
<div v-if="projektarbeit">
<h5>{{$capitalize( $p.t('abgabetool/c4abgabeStudentenbereich') )}}</h5>
<h5>{{$capitalize( $p.t('abgabetool/c4abgabeStudentenbereichv2') )}}</h5>
<div class="row">
<div class="col-8">
<p> {{$capitalize( $p.t('person/student') ) }}: {{projektarbeit?.student}}</p>
<p> {{$capitalize( $p.t('abgabetool/c4titel') ) }}: {{projektarbeit?.titel}}</p>
<p> {{$capitalize( $p.t('abgabetool/c4betreuerv2') ) }}: {{projektarbeit ? $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) + ' ' + projektarbeit.betreuer : ''}}</p>
<p>{{$capitalize( $p.t('person/student') ) }}: {{projektarbeit?.student}}</p>
<p class="d-flex align-items-center gap-2 mb-2" style="min-width: 0;">
<span
:title="projektarbeit.titel"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 480px;"
>{{$capitalize( $p.t('abgabetool/c4titel') ) }}: {{projektarbeit?.titel}}</span>
<button
v-if="isTitelEditAllowed"
class="btn btn-sm btn-outline-secondary border-0 p-1"
v-tooltip.right="{ value: $capitalize($p.t('abgabetool/c4titelBearbeiten')), class: 'custom-tooltip' }"
@click="openTitelEdit"
>
<i class="fa-solid fa-pen"></i>
</button>
</p>
<p>{{$capitalize( $p.t('abgabetool/c4betreuerv2') ) }}: {{projektarbeit ? $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) + ' ' + projektarbeit.betreuer : ''}}</p>
</div>
<div class="col-4">
<p>{{ $p.t('abgabetool/c4checkoutStgMoodleInfos') }}
<a :href="getMoodleLink" target="_blank">Moodle</a>
</p>
<div class="row">
<p>{{ $p.t('abgabetool/c4checkoutStgMoodleInfos') }}
<a :href="getMoodleLink" target="_blank">Moodle</a>
</p>
</div>
<div class="row" v-if="getSignaturInfoAvailable">
<a :href="getSignaturInfoLink" target="_blank">{{$p.t('abgabetool/c4signaturinfo')}} <i class="fa-solid fa-circle-info"></i></a>
</div>
</div>
</div>
@@ -357,7 +459,6 @@ export const AbgabeStudentDetail = {
<i v-else-if="termin.dateStyle == 'beurteilungerforderlich'" v-tooltip.right="getTooltipBeurteilungerforderlich" class="fa-solid fa-list-check"></i>
<i v-else-if="termin.dateStyle == 'bestanden'" v-tooltip.right="getTooltipBestanden" class="fa-solid fa-check"></i>
<i v-else-if="termin.dateStyle == 'nichtbestanden'" v-tooltip.right="getTooltipNichtBestanden" class="fa-solid fa-circle-exclamation"></i>
</div>
<div class="text-start px-2" style="min-width: 150px; max-width: 300px; margin-left: 40px">
<span>{{ termin ? $p.t('abgabetool/c4paatyp' + termin.paabgabetyp_kurzbz) : '' }}</span>
@@ -408,8 +509,6 @@ export const AbgabeStudentDetail = {
</div>
</template>
</Inplace>
</div>
<div class="row mt-2">
@@ -430,7 +529,7 @@ export const AbgabeStudentDetail = {
</VueDatePicker>
</div>
</div>
<div class="row mt-2">
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabetyp') )}}</div>
<div class="col-12 col-md-9">
@@ -466,7 +565,7 @@ export const AbgabeStudentDetail = {
<div class="col-12 col-md-9">
<template v-if="termin?.abgabedatum">
<div class="row">
<div style="width:100px; align-content: center;">
<div style="width:100px; align-content: end;">
<h6>{{ termin.abgabedatum?.split("-").reverse().join(".") }}</h6>
</div>
@@ -481,9 +580,6 @@ export const AbgabeStudentDetail = {
<Message v-else-if="termin?.signatur == false" severity="error" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4keineSignatur') }} </Message>
<Message v-else-if="termin?.signatur == 'error'" severity="warn" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4signaturServerError') }} </Message>
</div>
<!-- <div v-else class="col-auto">-->
<!-- <Message severity="info" :closable="false" :pt="getMessagePtStyle"> {{ $p.t('abgabetool/c4noFileFound') }} </Message>-->
<!-- </div>-->
</template>
</div>
</template>
@@ -542,25 +638,66 @@ export const AbgabeStudentDetail = {
<h5>{{ $capitalize( $p.t('abgabetool/c4keineAbgabetermineGefunden') )}}</h5>
</div>
</div>
</div>
<div v-if="confetti_on_endupload" id="confetti-container"></div>
<bs-modal
ref="modalTitelEdit"
class="bootstrap-prompt"
dialogClass="bordered-modal"
bodyClass="px-4 py-4"
>
<template v-slot:title>
{{$capitalize( $p.t('abgabetool/c4titelBearbeiten') )}}
</template>
<template v-slot:default>
<div class="mb-2">
<label class="form-label fw-bold">
{{$capitalize( $p.t('abgabetool/c4titel') )}}
</label>
<Textarea
v-model="editingTitel"
rows="10"
maxlength="1024"
class="form-control w-100"
@keydown.enter.prevent="saveTitel"
/>
<div class="form-text text-end">{{ editingTitel.length }} / 1024</div>
</div>
</template>
<template v-slot:footer>
<button
class="btn btn-secondary"
@click="$refs.modalTitelEdit.hide()"
>
{{$capitalize( $p.t('abgabetool/c4Cancel') )}}
</button>
<button
class="btn btn-primary"
:disabled="!editingTitel.trim()"
@click="saveTitel"
>
<i class="fa-solid fa-floppy-disk me-1"></i>
{{$capitalize( $p.t('ui/speichern') )}}
</button>
</template>
</bs-modal>
<bs-modal
ref="modalContainerEnduploadZusatzdaten"
class="bootstrap-prompt"
dialogClass="bordered-modal modal-lg">
dialogClass="bordered-modal modal-lg"
bodyClass="px-4 py-4">
<template v-slot:title>
<div>
{{$capitalize( $p.t('abgabetool/c4enduploadZusatzdaten') )}}
</div>
<div class="row mb-3 align-items-start">
<p class="ml-4 mr-4">Student UID: {{ projektarbeit?.student_uid}}</p>
</div>
<div class="row mb-3 align-items-start">
<p class="ml-4 mr-4">{{$capitalize( $p.t('abgabetool/c4titel') )}}: {{ projektarbeit?.titel }}</p>
</div>
</template>
<template v-slot:default>
@@ -576,15 +713,6 @@ export const AbgabeStudentDetail = {
</div>
</div>
<!-- lektor fills these out-->
<!-- <div class="row mb-3 align-items-start">-->
<!-- <div class="row">Kontrollierte Schlagwörter</div>-->
<!-- <div class="row">-->
<!-- <Textarea v-model="form.kontrollschlagwoerter"></Textarea>-->
<!-- </div>-->
<!-- -->
<!-- -->
<!-- </div>-->
<div class="row mb-3 align-items-start">
<div class="row">{{$capitalize( $p.t('abgabetool/c4schlagwoerterGer') )}}</div>
<div class="row">
@@ -631,7 +759,6 @@ export const AbgabeStudentDetail = {
<div class="col-9"></div>
<div class="col-2"><p>{{$capitalize( $p.t('abgabetool/c4gelesenUndAkzeptiert') )}}</p></div>
<div class="col-1">
<Checkbox
v-model="eidAkzeptiert"
:binary="true"
@@ -647,8 +774,8 @@ export const AbgabeStudentDetail = {
<div v-show="!allowedToSaveZusatzdaten">{{ $p.t('abgabetool/c4zusatzdatenausfuellen') }}</div>
<button class="btn btn-primary" :disabled="!getAllowedToSendEndupload" @click="triggerEndupload">{{$capitalize( $p.t('ui/hochladen') )}}</button>
</template>
</bs-modal>
`,
};
File diff suppressed because it is too large Load Diff
@@ -7,6 +7,7 @@ import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass } from "./getDateStyleClass.js";
import { dateFilter } from '../../../tabulator/filters/Dates.js';
import {splitMailsHelper} from "../../../helpers/EmailHelpers.js";
import { formatISODate, getViennaTodayISO, toViennaDate } from "./dateUtils.js";
export const AbgabetoolMitarbeiter = {
name: "AbgabetoolMitarbeiter",
@@ -31,19 +32,14 @@ export const AbgabetoolMitarbeiter = {
old_abgabe_beurteilung_link: Vue.computed(() => this.old_abgabe_beurteilung_link)
}
},
props: {
viewData: {
type: Object,
required: true,
default: () => ({name: '', uid: ''}),
validator(value) {
return value && value.uid // && value.name -> extensive viewData use only for cis4 onwards
}
}
},
data() {
return {
tableData: null,
filteredRows: null,
count: 0,
filteredcount: 0,
selectedcount: 0,
qgate1FilterSelected: [],
qgate2FilterSelected: [],
abgabetypenBetreuer: null,
detailIsFullscreen: false,
phrasenPromise: null,
@@ -58,7 +54,7 @@ export const AbgabetoolMitarbeiter = {
allowedNotenOptions: null,
notenOptionsNonFinal: null,
serienTermin: Vue.reactive({
datum: new Date(),
datum: getViennaTodayISO(),
bezeichnung: {
paabgabetyp_kurzbz: 'zwischen',
bezeichnung: 'Zwischenabgabe'
@@ -80,7 +76,7 @@ export const AbgabetoolMitarbeiter = {
abgabeTableOptions: {
minHeight: 250,
index: 'projektarbeit_id',
layout: 'fitDataStretch',
layout: 'fitData',
placeholder: Vue.computed(() => this.$p.t('global/noDataAvailable')),
selectable: true,
selectableCheck: this.selectionCheck,
@@ -138,38 +134,65 @@ export const AbgabetoolMitarbeiter = {
handleClick: this.selectAllHandler
},
width: 50,
cssClass: 'sticky-col'
cssClass: 'sticky-col',
visible: true
},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, headerFilter: false, headerSort: false, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, maxWidth: 500, widthGrow: 8},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4betreuerartv2'))), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))), field: 'prevTermin',
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.formAction, headerFilter: false, headerSort: false, minWidth: 85, visible: true, tooltip: false, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, minWidth: 140, visible: false,tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100,visible: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100,visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, minWidth: 100,visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 50, visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, visible: true, minWidth: 100},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100, width: 500, visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4betreuerartv2'))), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, visible: true, minWidth: 100, width: 200},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))),
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
tooltip: this.toolTipFuncPrevTermin,
field: 'prevTermin', formatter: this.abgabterminFormatter, width: 250, visible: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
tooltip: this.toolTipFuncNextTermin,
formatter: this.abgabterminFormatter, width: 250, visible: true},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList },
field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
headerFilter: this.qgateHeaderFilterEditor,
headerFilterFunc: this.qgateHeaderFilterFunc,
headerFilterParams: {},
field: 'qgate1Status',
formatter: this.centeredTextFormatter,
titleFormatter: this.shortLongTitleFormatter,
titleFormatterParams: {
shortForm: 'QG1'
},
width: 50,
tooltip: (e, cell) => {
const data = cell.getData();
return data.qgate1Status
}
},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))),
headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList },
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}
headerFilter: this.qgateHeaderFilterEditor,
headerFilterFunc: this.qgateHeaderFilterFunc,
headerFilterParams: {},
field: 'qgate2Status',
formatter: this.centeredTextFormatter,
titleFormatter: this.shortLongTitleFormatter,
titleFormatterParams: {
shortForm: 'QG2'
},
width: 50,
tooltip: (e, cell) => {
const data = cell.getData();
return data.qgate2Status
}
}
],
persistence: false,
persistenceID: 'abgabeTableBetreuer2026-02-26'
persistenceID: 'abgabeTableBetreuer2026-05-26'
},
abgabeTableEventHandlers: [{
event: "tableBuilt",
@@ -200,11 +223,364 @@ export const AbgabetoolMitarbeiter = {
})
this.selectedData = data
this.selectedcount = data.length;
}
},
{
event: 'dataFiltered',
handler: (filters, rows) => {
this.filteredRows = rows;
this.filteredcount = rows.length;
if (!this.selectedData.length) return;
const visibleData = new Set(rows.map(r => r.getData()));
const filteredOut = this.selectedData.filter(sd => !visibleData.has(sd));
if (!filteredOut.length) return;
const filteredOutSet = new Set(filteredOut);
this.$refs.abgabeTable.tabulator.getSelectedRows()
.filter(r => filteredOutSet.has(r.getData()))
.forEach(r => r.deselect());
}
}
]};
},
methods: {
async openBenotung(type, link) {
if(type === 'new') {
window.open(link, '_blank')
} else if(type === 'old') {
if(await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4aeltereParbeitBenotenv2'),
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
acceptClass: 'btn btn-danger',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'btn btn-outline-secondary'
}) === false) {
return false
}
window.open(link, '_blank')
} else {
// show info text that no endupload with abgabe has been found
if(await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4keinEnduploadErfolgt'),
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
acceptClass: 'btn btn-danger',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'btn btn-outline-secondary'
}) === false) {
return false
}
}
},
formAction(cell) {
const actionButtons = document.createElement('div');
actionButtons.className = "d-flex gap-3";
actionButtons.style.display = "flex";
actionButtons.style.alignItems = "stretch";
actionButtons.style.justifyContent = "start";
actionButtons.style.height = "100%";
const val = cell.getValue();
const data = cell.getRow().getData()
const createButton = (iconClass, titleKey, clickHandler) => {
const btn = document.createElement('button');
btn.className = 'btn btn-outline-secondary';
btn.style.display = "flex";
btn.style.alignItems = "center"; // center icon vertically
btn.style.justifyContent = "center"; // center icon horizontally
btn.style.height = "100%"; // fill parent container height
btn.style.aspectRatio = "1 / 1"; // keep square shape (optional)
btn.style.padding = "0"; // remove extra padding for compactness
if(iconClass == 'fa fa-timeline') btn.style.transform = "rotate(90deg)";
btn.innerHTML = `<i class="${iconClass}" style="color:#00649C; font-size:1.1rem;"></i>`;
btn.title = this.$capitalize(this.$p.t(titleKey));
btn.addEventListener('click', (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
clickHandler();
});
return btn;
};
actionButtons.append(
createButton('fa fa-folder-open', 'abgabetool/c4details', () => this.setDetailComponent(val)),
);
if(data.isCurrent && data.abgabetermine?.find(termin => termin.paabgabetyp_kurzbz == 'end' && termin.abgabedatum !== null) && data.beurteilungLinkNew) {
actionButtons.append(createButton('fa fa-user-check', 'abgabetool/c4benoten', () => this.openBenotung('new', data.beurteilungLinkNew)))
} else if(data.abgabetermine?.find(termin => termin.paabgabetyp_kurzbz == 'end' && termin.abgabedatum !== null) && data.beurteilungLinkOld) {
actionButtons.append(createButton('fa fa-user-check', 'abgabetool/c4benoten', () => this.openBenotung('old', data.beurteilungLinkOld)))
}
return actionButtons;
},
getDateStyleHtml(dateStyle) {
const iconMap = {
'verspaetet': '<i class="fa-solid fa-triangle-exclamation"></i>',
'verpasst': '<i class="fa-solid fa-calendar-xmark"></i>',
'abzugeben': '<i class="fa-solid fa-hourglass-half"></i>',
'standard': '<i class="fa-solid fa-clock"></i>',
'abgegeben': '<i class="fa-solid fa-paperclip"></i>',
'beurteilungerforderlich': '<i class="fa-solid fa-list-check"></i>',
'bestanden': '<i class="fa-solid fa-check"></i>',
'nichtbestanden': '<i class="fa-solid fa-circle-exclamation"></i>',
};
return iconMap[dateStyle] ?? '';
},
statusHeaderFilterEditor(cell, onRendered, success, cancel, editorParams) {
const options = [
{ label: this.$p.t('abgabetool/c4positivBenotet'), value: 'bestanden', dateStyle: 'bestanden' },
{ label: this.$p.t('abgabetool/c4negativBenotet'), value: 'nichtbestanden', dateStyle: 'nichtbestanden' },
{ label: this.$p.t('abgabetool/c4tooltipVerspaetet'), value: 'verspaetet', dateStyle: 'verspaetet' },
{ label: this.$p.t('abgabetool/c4tooltipVerpasst'), value: 'verpasst', dateStyle: 'verpasst' },
{ label: this.$p.t('abgabetool/c4tooltipAbzugeben'), value: 'abzugeben', dateStyle: 'abzugeben' },
{ label: this.$p.t('abgabetool/c4tooltipAbgegeben'), value: 'abgegeben', dateStyle: 'abgegeben' },
{ label: this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich'), value: 'beurteilungerforderlich', dateStyle: 'beurteilungerforderlich' },
{ label: this.$p.t('abgabetool/c4tooltipStandardv2'), value: 'standard', dateStyle: 'standard' },
];
const field = cell.getField();
const stateKey = field + 'FilterSelected'; // e.g. dateStyleFilterSelected
let selected = [...(this[stateKey] || [])];
const wrapper = document.createElement('div');
wrapper.style.cssText = 'position: relative; width: 100%;';
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 220px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
const updateDisplay = () => {
display.value = options
.filter(o => selected.includes(o.value))
.map(o => o.label)
.join(', ');
};
options.forEach(opt => {
const row = document.createElement('label');
row.style.cssText = 'display: flex; align-items: center; gap: 0; cursor: pointer; white-space: nowrap; padding-right: 8px;';
row.addEventListener('mousedown', e => e.preventDefault());
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.value = opt.value;
cb.checked = selected.includes(opt.value);
cb.style.cssText = 'margin: 0 6px;';
cb.addEventListener('change', () => {
selected = cb.checked
? [...selected, opt.value]
: selected.filter(v => v !== opt.value);
this[stateKey] = [...selected];
updateDisplay();
success([...selected]);
});
// icon badge — same look as cell
const badge = document.createElement('div');
badge.className = opt.dateStyle + '-header';
badge.style.cssText = `min-width: 36px; height: 36px; display: flex; align-items: center;
justify-content: center; flex-shrink: 0; padding: 0px 17px 0px 17px;`;
badge.innerHTML = this.getDateStyleHtml(opt.dateStyle);
const labelText = document.createElement('span');
labelText.textContent = opt.label;
labelText.style.cssText = 'margin-left: 6px;';
row.appendChild(cb);
row.appendChild(badge);
row.appendChild(labelText);
dropdown.appendChild(row);
});
updateDisplay();
display.addEventListener('click', () => {
if (dropdown.style.display === 'none') {
const rect = display.getBoundingClientRect();
dropdown.style.top = rect.bottom + 'px';
dropdown.style.left = rect.left + 'px';
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
});
display.addEventListener('blur', () => {
setTimeout(() => { dropdown.style.display = 'none'; }, 150);
});
document.body.appendChild(dropdown);
wrapper.appendChild(display);
cell.getElement().addEventListener('remove', () => dropdown.remove());
onRendered(() => display.focus());
return wrapper;
},
statusHeaderFilterFunc(filterVal, rowVal, rowData, filterParams) {
if (!filterVal || !filterVal.length) return true;
// rowVal is the raw dateStyle string on the flat table
return filterVal.some(val => val === rowVal);
},
qgateHeaderFilterEditor(cell, onRendered, success, cancel, editorParams) {
const options = [
{ label: '[+] ' + this.$p.t('abgabetool/c4positivBenotet'), value: 'positive' },
{ label: '[-] ' + this.$p.t('abgabetool/c4negativBenotet'), value: 'negative' },
{ label: '[~] ' + this.$p.t('abgabetool/c4notYetGraded'), value: 'not_graded' },
{ label: '[?] ' + this.$p.t('abgabetool/c4notSubmitted'), value: 'not_submitted' },
{ label: '[o] ' + this.$p.t('abgabetool/c4notHappenedYet'), value: 'not_happened' },
{ label: '[--] ' + this.$p.t('abgabetool/c4keinTerminVorhanden'), value: 'no_termin' },
];
const field = cell.getField();
const stateKey = field === 'qgate1Status' ? 'qgate1FilterSelected' : 'qgate2FilterSelected';
let selected = [...(this[stateKey] || [])]; // restore persistence state
const wrapper = document.createElement('div');
wrapper.style.cssText = 'position: relative; width: 100%;';
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
options.forEach(opt => {
const row = document.createElement('label');
row.style.cssText = 'display: flex; align-items: center; gap: 6px; padding: 4px 8px; cursor: pointer; white-space: nowrap;';
row.addEventListener('mousedown', e => e.preventDefault());
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.value = opt.value;
cb.checked = selected.includes(opt.value); // sync with persistence
cb.addEventListener('change', () => {
if (cb.checked) {
selected.push(opt.value);
} else {
selected = selected.filter(v => v !== opt.value);
}
this[stateKey] = [...selected]; // sync with persistence
display.value = options.filter(o => selected.includes(o.value)).map(o => o.label).join(', ');
success([...selected]);
});
row.appendChild(cb);
row.appendChild(document.createTextNode(opt.label));
dropdown.appendChild(row);
});
display.value = options.filter(o => selected.includes(o.value)).map(o => o.label).join(', ');
display.addEventListener('click', () => {
if (dropdown.style.display === 'none') {
const rect = display.getBoundingClientRect();
dropdown.style.top = rect.bottom + 'px';
dropdown.style.left = rect.left + 'px';
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
});
display.addEventListener('blur', () => {
setTimeout(() => { dropdown.style.display = 'none'; }, 150);
});
document.body.appendChild(dropdown);
wrapper.appendChild(display);
cell.getElement().addEventListener('remove', () => dropdown.remove());
onRendered(() => display.focus());
return wrapper;
},
qgateHeaderFilterFunc(filterVal, rowVal, rowData, filterParams) {
if (!filterVal || !filterVal.length) return true;
const matches = (val) => {
switch (val) {
case 'positive': return rowVal === this.$p.t('abgabetool/c4positivBenotet');
case 'negative': return rowVal === this.$p.t('abgabetool/c4negativBenotet');
case 'not_graded': return rowVal === this.$p.t('abgabetool/c4notYetGraded');
case 'not_submitted':return rowVal === this.$p.t('abgabetool/c4notSubmitted');
case 'not_happened': return rowVal === this.$p.t('abgabetool/c4notHappenedYet');
case 'no_termin': return rowVal === this.$p.t('abgabetool/c4keinTerminVorhanden');
default: return true;
}
};
// OR logic — row passes if it matches any selected filter
return filterVal.some(val => matches(val));
},
shortLongTitleFormatter(cell, formatterParams, onRendered) {
const longForm = cell.getValue()
const shortForm = formatterParams?.shortForm
if(longForm && shortForm) {
return `<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>
<span class="short-text" style="font-weight: bold; display: none;">
${shortForm}
</span>`
} else {
return `<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>`
}
},
toolTipFuncPrevTermin(e, cell, onRendered) {
const data = cell.getData();
return this.mapDateStyleToTabulatorTooltip(data.prevTermin.dateStyle);
},
toolTipFuncNextTermin(e, cell, onRendered) {
const data = cell.getData();
return this.mapDateStyleToTabulatorTooltip(data.nextTermin.dateStyle);
},
mapDateStyleToTabulatorTooltip(dateStyleString) {
switch(dateStyleString) {
case 'bestanden':
return this.$p.t('abgabetool/c4tooltipBestanden')
break;
case 'nichtbestanden':
return this.$p.t('abgabetool/c4tooltipNichtBestanden')
break;
case 'beurteilungerforderlich':
return this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich')
break;
case 'verspaetet':
return this.$p.t('abgabetool/c4tooltipVerspaetet')
break;
case 'abgegeben':
return this.$p.t('abgabetool/c4tooltipAbgegeben')
break;
case 'verpasst':
return this.$p.t('abgabetool/c4tooltipVerpasst')
break;
case 'abzugeben':
return this.$p.t('abgabetool/c4tooltipAbzugeben')
break;
case 'standard':
return this.$p.t('abgabetool/c4tooltipStandardv2')
break;
default: return ''
}
},
handlePaUpdated(projektarbeit) {
this.checkAbgabetermineProjektarbeit(projektarbeit)
this.$refs.abgabeTable.tabulator.redraw(true)
@@ -217,7 +593,7 @@ export const AbgabetoolMitarbeiter = {
})
const uniqueRecipients = [...new Set(recipientList)];
const subject = ""; // empty subject line
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, null, this.$fhcAlert, this.$p)
},
getQGateStatusList() {
return [
@@ -257,7 +633,7 @@ export const AbgabetoolMitarbeiter = {
if (val instanceof Date) {
dt = luxon.DateTime.fromJSDate(val);
} else if (typeof val === "string") {
dt = luxon.DateTime.fromISO(val);
dt = toViennaDate(val);
} else { // fallback
dt = luxon.DateTime.fromMillis(Number(val));
}
@@ -386,6 +762,9 @@ export const AbgabetoolMitarbeiter = {
}
this.stateRestored = true
// ensure that the filterCollapseables thingy has the correct values
this.$refs.abgabeTable.setSelectedFields();
}
});
@@ -451,6 +830,32 @@ export const AbgabetoolMitarbeiter = {
projekt.qgate2StatusRank = 1
}
})
// set shorthand statuscode once real status has been determined
projekt.qgate1StatusShort = this.mapRankToShortStatus(projekt.qgate1StatusRank)
projekt.qgate2StatusShort = this.mapRankToShortStatus(projekt.qgate2StatusRank)
},
mapRankToShortStatus(rank) {
switch(rank){
case 0: // kein termin vorhanden
return '--'
break;
case 1: // noch nicht stattgefunden
return 'o'
break;
case 2: // noch nicht abgegeben
return '?'
break;
case 3: // noch nicht benotet
return '~'
break;
case 4: // negativ benotet
return '-'
break;
case 5: // positiv benotet
return '+'
break;
}
},
checkAbgabetermineProjektarbeit(projekt) {
const now = luxon.DateTime.now()
@@ -460,7 +865,7 @@ export const AbgabetoolMitarbeiter = {
// while already looping through each termin, calculate datestyle beforehand
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
const date = luxon.DateTime.fromISO(termin.datum).endOf('day')
const date = toViennaDate(termin.datum).endOf('day')
termin.luxonDate = date
termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past
@@ -517,11 +922,11 @@ export const AbgabetoolMitarbeiter = {
const bezeichnung = val.bezeichnung?.bezeichnung ?? val.bezeichnung
return '<div style="display: flex; height: 100%">' +
'<div class=' + val.dateStyle + "-header" + ' style="width:48px; height: 100%; padding: 0px; display: flex; align-items: center; justify-content: center;">' +
icon +
'<div class=' + val.dateStyle + "-header" + ' style="min-width:48px; height: 100%; padding: 0px; display: flex; align-items: center; justify-content: center;">' +
icon +
'</div>' +
'<div style="margin-left: 4px;">' +
'<p style="max-width: 100%; word-wrap: break-word; white-space: normal;">'+bezeichnung+' - '+ this.formatDate(val.datum)+'</p>' +
'<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;">'+bezeichnung+' - '+ this.formatDate(val.datum)+'</p>' +
'</div>'+
'</div>'
@@ -545,16 +950,19 @@ export const AbgabetoolMitarbeiter = {
},
selectAllHandler(e, cell) {
const table = cell.getTable();
const rows = table.getRows();
const rows = this.filteredRows ?? table.getRows();
// custom select all logic
const allowed = rows.filter(r => r.getData().selectable);
// since betreuerpage acctually has logic behind selectable flag, it is important to go over allowed only here
const selected = allowed.every(r => r.isSelected());
if(selected) {
if(selected){
allowed.forEach(r => r.deselect());
e.target.checked = false;
} else {
allowed.forEach(r => r.select());
e.target.checked = true;
}
// stop built-in handler
@@ -568,15 +976,7 @@ export const AbgabetoolMitarbeiter = {
return option.bezeichnung
},
formatDate(dateParam) {
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`;
return formatISODate(dateParam);
},
undoSelection(cell) {
// checks if cells row is selected and unselects -> imitates columns which dont trigger row selection
@@ -589,6 +989,8 @@ export const AbgabetoolMitarbeiter = {
},
selectionCheck(row) {
const data = row.getData()
// zweitbetreuer cant select projektarbeiten for serientermine
if(data?.betreuerart_kurzbz == 'Zweitbegutachter') return false
return true
},
@@ -612,7 +1014,7 @@ export const AbgabetoolMitarbeiter = {
addSeries() {
this.saving = true
this.$api.call(ApiAbgabe.postSerientermin(
this.serienTermin.datum.toISOString(),
this.serienTermin.datum,
this.serienTermin.bezeichnung.paabgabetyp_kurzbz,
this.serienTermin.bezeichnung.bezeichnung,
this.serienTermin.kurzbz,
@@ -710,7 +1112,7 @@ export const AbgabetoolMitarbeiter = {
termin.allowedToSave = paIsBenotet ? false : true
// lektoren are not allowed to delete deadlines with existing submissions
termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum
termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum && !termin.note
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
@@ -727,28 +1129,37 @@ export const AbgabetoolMitarbeiter = {
},
centeredTextFormatter(cell) {
const val = cell.getValue()
if(!val) return
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<p style="max-width: 100%; width: 100%; overflow-wrap: break-word; word-break: break-word; white-space: normal; margin: 0px; text-align: center">'+val+'</p></div>'
const longForm = cell.getValue()
if(!longForm) return
const data = cell.getData()
const entry = Object.entries(data).find(entry => entry[1] == longForm)
// shortFormKey must have same keyname as longForm but with 'Short' appended
const shortForm = data[entry[0]+'Short']
if(shortForm && longForm) {
return `<div style="display: flex; justify-content: start; align-items: center; height: 100%; width: 100%;">
<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>
<span class="short-text" style="font-weight: bold; display: none;">
${shortForm}
</span>
</div>`;
} else {
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">'+longForm+'</p></div>'
}
},
detailFormatter(cell) {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) {
const val = cell.getValue()
if(val) {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a><i class="fa fa-file-pdf" style="color:#00649C"></i></a></div>'
} else return '-'
},
pkzTextFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a style="max-width: 100%; word-wrap: break-word; white-space: normal;">'+val+'</a></div>'
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<a style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;">'+val+'</a></div>'
},
tableResolve(resolve) {
this.tableBuiltResolve = resolve
@@ -765,10 +1176,9 @@ export const AbgabetoolMitarbeiter = {
setupData(data){
this.projektarbeiten = data[0]
this.domain = data[1]
this.tableData = data[0]?.retval?.map(projekt => {
this.projektarbeiten = data[0]?.retval?.map(projekt => {
this.checkAbgabetermineProjektarbeit(projekt)
projekt.selectable = projekt.betreuerart_kurzbz !== 'Zweitbegutachter'
@@ -787,9 +1197,10 @@ export const AbgabetoolMitarbeiter = {
titel: projekt.titel
}
})
this.count = this.projektarbeiten.length
this.$refs.abgabeTable.tabulator.setColumns(this.abgabeTableOptions.columns)
this.$refs.abgabeTable.tabulator.setData(this.tableData);
this.$refs.abgabeTable.tabulator.setData(this.projektarbeiten);
},
loadProjektarbeiten(all = false, callback) {
this.$api.call(ApiAbgabe.getMitarbeiterProjektarbeiten(all))
@@ -841,6 +1252,17 @@ export const AbgabetoolMitarbeiter = {
},
},
computed: {
countsToHTML() {
return this.$p.t('global/ausgewaehlt')
+ ': <strong>' + (this.selectedcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gefiltert')
+ ': '
+ '<strong>' + (this.filteredcount || 0) + '</strong>'
+ ' | '
+ this.$p.t('global/gesamt')
+ ': <strong>' + (this.count || 0) + '</strong>';
},
emailItems() {
const menu = []
@@ -869,6 +1291,8 @@ export const AbgabetoolMitarbeiter = {
}
},
created() {
document.documentElement.classList.add('abgabetool');
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
// fetch config to avoid hard coded links
@@ -910,12 +1334,16 @@ export const AbgabetoolMitarbeiter = {
mounted() {
this.setupMounted()
},
beforeUnmount() {
document.documentElement.classList.remove('abgabetool');
},
template: `
<template v-if="phrasenResolved">
<FhcOverlay :active="loading || saving"></FhcOverlay>
<bs-modal ref="modalContainerAddSeries" class="bootstrap-prompt"
dialogClass="modal-lg">
dialogClass="modal-lg"
bodyClass="px-4 py-4">
<template v-slot:title>
<div>
{{ $p.t('abgabetool/neueTerminserie') }}
@@ -935,6 +1363,7 @@ export const AbgabetoolMitarbeiter = {
:enable-time-picker="false"
locale="de"
format="dd.MM.yyyy"
model-type="yyyy-MM-dd"
:text-input="true"
auto-apply>
</VueDatePicker>
@@ -982,7 +1411,8 @@ export const AbgabetoolMitarbeiter = {
<bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt"
dialogClass="modal-xl" :allowFullscreenExpand="true"
@toggle-fullscreen="handleToggleFullscreenDetail">
@toggle-fullscreen="handleToggleFullscreenDetail"
bodyClass="px-4 py-4">
<template v-slot:title>
<div>
{{$p.t('abgabetool/c4abgabeMitarbeiterDetailTitle')}}
@@ -1001,12 +1431,13 @@ export const AbgabetoolMitarbeiter = {
<!-- low max height on this vsplit wrapper to avoid padding scrolls, elements have their inherent height anyways -->
<div id="abgabetable" style="max-height:40vw;">
<h2>{{$p.t('abgabetool/abgabetoolTitle')}}</h2>
<h2>{{$p.t('abgabetool/abgabetoolTitleBetreuer')}}</h2>
<hr>
<core-filter-cmpt
:title="''"
@uuidDefined="handleUuidDefined"
ref="abgabeTable"
:description="countsToHTML"
:newBtnShow="true"
:newBtnLabel="$p.t('abgabetool/neueTerminserie')"
:newBtnDisabled="!selectedData.length"
@@ -1,14 +1,17 @@
import AbgabeDetail from "./AbgabeStudentDetail.js";
import ApiAbgabe from '../../../api/factory/abgabe.js'
import ApiAuthinfo from '../../../api/factory/authinfo.js';
import BsModal from "../../Bootstrap/Modal.js";
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass} from "./getDateStyleClass.js";
import { validateThesisTitle } from './titleValidation.js'
export const AbgabetoolStudent = {
name: "AbgabetoolStudent",
components: {
Accordion: primevue.accordion,
AccordionTab: primevue.accordiontab,
Textarea: primevue.textarea,
BsModal,
AbgabeDetail,
FhcOverlay
@@ -17,20 +20,16 @@ export const AbgabetoolStudent = {
return {
notenOptions: Vue.computed(() => this.notenOptions),
isViewMode: Vue.computed(() => this.isViewMode),
moodle_link: Vue.computed(() => this.moodle_link)
moodle_link: Vue.computed(() => this.moodle_link),
title_edit_allowed: Vue.computed(() => this.title_edit_allowed),
confetti_on_endupload: Vue.computed(() => this.confetti_on_endupload),
siginfolink_german: Vue.computed(() => this.siginfolink_german),
siginfolink_english: Vue.computed(() => this.siginfolink_english)
}
},
props: {
student_uid_prop: {
default: null
},
viewData: {
type: Object,
required: true,
default: () => ({uid: ''}),
validator(value) {
return value && value.uid
}
}
},
data() {
@@ -44,14 +43,78 @@ export const AbgabetoolStudent = {
detail: null,
projektarbeiten: null,
selectedProjektarbeit: null,
moodle_link: null
moodle_link: null,
title_edit_allowed: null,
confetti_on_endupload: null,
siginfolink_german: null,
siginfolink_english: null,
editingTitel: '',
editingProjektarbeit: null,
uid: null
};
},
methods: {
openTitelEdit(projektarbeit, event) {
// stop the click from toggling the accordion tab
event.stopPropagation();
this.editingProjektarbeit = projektarbeit;
this.editingTitel = projektarbeit.titel ?? '';
this.$refs.modalTitelEdit.show();
},
async saveTitel() {
const validation = validateThesisTitle(this.editingTitel);
if (!validation.isValid) {
if (validation.error === 'empty') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4emptyThesisTitle'))
} else if (validation.error === 'invalid_characters') {
this.$fhcAlert.alertWarning(this.$p.t('abgabetool/c4invalidCharactersThesisTitle'))
}
return false;
}
const confirmed = await this.$fhcAlert.confirm({
message: this.$p.t('abgabetool/c4confirmTitelSpeichern'),
acceptLabel: this.$capitalize(this.$p.t('ui/speichern')),
acceptClass: 'p-button-primary',
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
rejectClass: 'p-button-secondary'
});
if (confirmed === false) return;
this.loading = true;
this.$api.call(
ApiAbgabe.postStudentProjektarbeitTitel(
this.editingProjektarbeit.projektarbeit_id,
validation.cleanedTitle
)
).then(res => {
if (res.meta.status === 'success') {
// update the local list entry in-place so the accordion header reflects it immediately
this.editingProjektarbeit.titel = res.data;
// keep the open detail modal in sync if it happens to be showing this projektarbeit
if (this.selectedProjektarbeit?.projektarbeit_id === this.editingProjektarbeit.projektarbeit_id) {
this.selectedProjektarbeit.titel = res.data;
}
this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4titelSavedSuccess')));
this.$refs.modalTitelEdit.hide();
} else {
this.$fhcAlert.alertError(this.$capitalize(this.$p.t('abgabetool/c4titelSaveError')));
}
}).finally(() => {
this.loading = false;
});
},
handleTitelUpdated(projektarbeit_id, titel) {
const pa = this.projektarbeiten?.find(p => p.projektarbeit_id === projektarbeit_id);
if (pa) pa.titel = titel;
},
checkQualityGatesStrict(termine) {
let qgate1Passed = false
let qgate2Passed = false
termine.forEach(t => {
const noteOption = this.notenOptions?.find(opt => opt.note == t.note)
if(noteOption && noteOption.positiv) {
@@ -68,7 +131,7 @@ export const AbgabetoolStudent = {
checkQualityGatesOptional(termine) {
const qgate1found = termine.find(t => t.paabgabetyp_kurzbz == 'qualgate1')
const qgate2found = termine.find(t => t.paabgabetyp_kurzbz == 'qualgate2')
let qgate1positiv = true
if(qgate1found) {
qgate1positiv = false
@@ -109,47 +172,35 @@ export const AbgabetoolStudent = {
this.loadAbgaben(details).then((res)=> {
const pa = this.projektarbeiten?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
pa.abgabetermine = res.data[0].retval
const paIsBenotet = pa.note !== null
pa.abgabetermine.forEach(termin => {
termin.file = []
termin.allowedToUpload = false
if(termin.paabgabetyp_kurzbz == 'end') {
// old assumed production logic when qgates are required
// termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesStrict(pa.abgabetermine)
const inTime = termin.fixtermin ? !this.isPastDate(termin.datum) : true
termin.allowedToUpload = inTime && this.checkQualityGatesOptional(pa.abgabetermine)
// development purposes
// termin.allowedToUpload = this.checkQualityGatesStrict(pa.abgabetermine)
// termin.allowedToUpload = true
} else if(termin.fixtermin) {
termin.allowedToUpload = !this.isPastDate(termin.datum)
} else {
// this could confuse people since we should dont show people this flag
termin.allowedToUpload = termin.upload_allowed
termin.allowedToUpload = termin.upload_allowed
}
// blocks client upload button if projektarbeitet is already beurteilt und thus further abgaben on any termin should be blocked
if(paIsBenotet) termin.allowedToUpload = false
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
})
pa.betreuer = this.buildBetreuer(pa)
pa.student_uid = this.student_uid
this.selectedProjektarbeit = pa
this.$refs.modalContainerAbgabeDetail.show()
}).finally(()=>{this.loading=false})
},
centeredTextFormatter(cell) {
@@ -171,8 +222,8 @@ export const AbgabetoolStudent = {
},
mailFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) {
const val = cell.getValue()
@@ -182,19 +233,17 @@ export const AbgabetoolStudent = {
} else return '-'
},
buildMailToLink(projekt) {
// should always be "projekt.mitarbeiter_uid +'@'+ this.domain", built in backend
return 'mailto:' + projekt.email
},
buildBetreuer(abgabe) {
return (abgabe.btitelpre ? abgabe.btitelpre + ' ' : '') + abgabe.bvorname + ' ' + abgabe.bnachname + (abgabe.btitelpost ? ' ' + abgabe.btitelpost : '')
},
async setupData(data){
// this.projektarbeiten = data[0]
const projektarbeiten = data[0] ?? null
if(!projektarbeiten) return
this.projektarbeiten = projektarbeiten.map(projekt => {
let mode = 'detailTermine'
return {
...projekt,
details: {
@@ -228,16 +277,14 @@ export const AbgabetoolStudent = {
.then(res => {
resolve(res)
})
})
})
},
async setupMounted() {
this.loadProjektarbeiten()
},
getAccTabHeaderForProjektarbeit(projektarbeit) {
let title = ''
title += projektarbeit.titel ?? this.$p.t('abgabetool/keinTitel')
return title
},
getMailLink(projektarbeit) {
@@ -260,23 +307,25 @@ export const AbgabetoolStudent = {
window.open(projektarbeit.beurteilung2)
}
},
watch: {
},
watch: {},
computed: {
isViewMode() {
return this.student_uid !== this.viewData.uid
return this.student_uid !== this.uid
},
student_uid() {
return this.student_uid_prop || this.viewData?.uid || null
return this.student_uid_prop || this.uid || null
}
},
async created() {
// make sure zoom media query doesnt spill ever to other CIS4 sites
document.documentElement.classList.add('abgabetool');
this.$api.call(ApiAuthinfo.getAuthUID()).then(res => this.uid = res.data.uid)
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
this.loading = true
//TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API
await this.$api.call(ApiAbgabe.getNoten()).then(res => {
if(res.meta.status == 'success') {
this.notenOptions = res.data[0]
@@ -289,16 +338,18 @@ export const AbgabetoolStudent = {
this.loading = false
})
// fetch abgabetypen options
this.$api.call(ApiAbgabe.getPaAbgabetypen()).then(res => {
this.abgabeTypeOptions = res.data
}).catch(e => {
this.loading = false
})
// fetch config to avoid hard coded links
this.$api.call(ApiAbgabe.getConfigStudent()).then(res => {
this.moodle_link = res.data?.moodle_link
this.title_edit_allowed = res.data?.title_edit_allowed
this.confetti_on_endupload = res.data?.confetti_on_endupload
this.siginfolink_german = res.data?.siginfolink_german
this.siginfolink_english = res.data?.siginfolink_english
}).catch(e => {
this.loading = false
})
@@ -306,26 +357,73 @@ export const AbgabetoolStudent = {
mounted() {
this.setupMounted()
},
beforeUnmount() {
document.documentElement.classList.remove('abgabetool');
},
template: `
<template v-if="phrasenResolved">
<FhcOverlay :active="loading"></FhcOverlay>
<bs-modal ref="modalContainerAbgabeDetail" class="bootstrap-prompt"
dialogClass="modal-xl" :allowFullscreenExpand="true">
dialogClass="modal-xl" :allowFullscreenExpand="true" bodyClass="px-4 py-4">
<template v-slot:title>
<div>
{{$capitalize( $p.t('abgabetool/c4abgabeStudentDetailTitle') )}}
</div>
</template>
<template v-slot:default>
<AbgabeDetail :projektarbeit="selectedProjektarbeit"></AbgabeDetail>
<AbgabeDetail
:projektarbeit="selectedProjektarbeit"
@titel-updated="handleTitelUpdated"
></AbgabeDetail>
</template>
</bs-modal>
<bs-modal
ref="modalTitelEdit"
class="bootstrap-prompt"
dialogClass="bordered-modal"
bodyClass="px-4 py-4"
>
<template v-slot:title>
{{$capitalize( $p.t('abgabetool/c4titelBearbeiten') )}}
</template>
<template v-slot:default>
<div class="mb-2">
<label class="form-label fw-bold">
{{$capitalize( $p.t('abgabetool/c4titel') )}}
</label>
<Textarea
v-model="editingTitel"
rows="10"
maxlength="1024"
class="form-control w-100"
@keydown.enter.prevent="saveTitel"
/>
<div class="form-text text-end">{{ editingTitel.length }} / 1024</div>
</div>
</template>
<template v-slot:footer>
<button
class="btn btn-secondary"
@click="$refs.modalTitelEdit.hide()"
>
{{$capitalize( $p.t('abgabetool/c4Cancel') )}}
</button>
<button
class="btn btn-primary"
:disabled="!editingTitel.trim()"
@click="saveTitel"
>
<i class="fa-solid fa-floppy-disk me-1"></i>
{{$capitalize( $p.t('ui/speichern') )}}
</button>
</template>
</bs-modal>
<h2>{{$capitalize( $p.t('abgabetool/abgabetoolTitle') )}}</h2>
<hr>
<div v-if="projektarbeiten === null">
<div v-if="projektarbeiten === null || projektarbeiten?.length == 0">
{{$capitalize( $p.t('abgabetool/c4abgabeStudentNoProjectsFound') )}}
</div>
@@ -335,8 +433,12 @@ export const AbgabetoolStudent = {
<template #header>
<div class="d-flex row w-100">
<div class="text-start" :class="projektarbeit.note != null ? 'col-6' : 'col-12'">
<span>{{getAccTabHeaderForProjektarbeit(projektarbeit)}}</span>
<div class="text-start" :class="projektarbeit.note != null ? 'col-6' : 'col-12'"
style="min-width: 0;">
<span
:title="getAccTabHeaderForProjektarbeit(projektarbeit)"
style="display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 600px;"
>{{getAccTabHeaderForProjektarbeit(projektarbeit)}}</span>
</div>
<div class="col-6 text-end">
<span>{{getNoteBezeichnung(projektarbeit)}}</span>
@@ -402,12 +504,33 @@ export const AbgabetoolStudent = {
{{ projektarbeit.projekttypbezeichnung }}
</div>
</div>
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4titel') )}}</div>
<div class="col-8 col-md-9">
{{ projektarbeit.titel }}
<div class="col-8 col-md-9 d-flex align-items-center gap-2" style="min-width: 0;">
<span
:title="projektarbeit.titel"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
>{{ projektarbeit.titel }}</span>
<button
v-if="title_edit_allowed && !isViewMode && projektarbeit.note == null"
class="btn btn-sm btn-outline-secondary border-0 p-1"
v-tooltip.right="{ value: $capitalize($p.t('abgabetool/c4titelBearbeiten')), class: 'custom-tooltip' }"
@click="openTitelEdit(projektarbeit, $event)"
>
<i class="fa-solid fa-pen"></i>
</button>
</div>
</div>
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4note') )}}</div>
<div class="col-8 col-md-9">
<span>{{getNoteBezeichnung(projektarbeit)}}</span>
</div>
</div>
</AccordionTab>
</template>
</Accordion>
@@ -415,4 +538,4 @@ export const AbgabetoolStudent = {
`,
};
export default AbgabetoolStudent;
export default AbgabetoolStudent;
@@ -0,0 +1,26 @@
const zone = 'Europe/Vienna';
export function getViennaTodayISO() {
return luxon.DateTime.now().setZone(zone).toISODate();
}
export function formatISODate(dateParam) {
if (!dateParam) return '';
const date = luxon.DateTime.fromISO(String(dateParam), { zone });
return date.isValid ? date.toFormat('dd.MM.yyyy') : '';
}
export function toViennaDate(dateParam) {
if (!dateParam) return null;
return luxon.DateTime.fromISO(String(dateParam), { zone });
}
export function compareISODateValues(a, b) {
if (!a && !b) return 0;
if (!a) return 1;
if (!b) return -1;
return String(a).localeCompare(String(b));
}
@@ -1,8 +1,8 @@
const zone = 'Europe/Vienna';
const today = luxon.DateTime.now().setZone(zone);
export function getDateStyleClass(termin, notenOptions) {
const today = luxon.DateTime.now().setZone(zone);
const datum = luxon.DateTime.fromISO(termin.datum, { zone }).endOf('day');
const abgabedatum = termin.abgabedatum ? luxon.DateTime.fromISO(termin.abgabedatum, { zone }) : null;
termin.diffindays = datum.diff(today, 'days').days;
@@ -28,10 +28,11 @@ export function getDateStyleClass(termin, notenOptions) {
// no submission yet
if (datum < today) return 'verpasst';
if (termin.diffindays <= 12) return 'abzugeben';
return 'standard';
}
// GENERIC STATUS
return datum < today ? 'verpasst' : 'standard';
}
// GENERIC STATUS — applies to all termine
if (datum < today) return 'verpasst';
if (termin.diffindays <= 12) return 'abzugeben';
return 'standard';
}
@@ -0,0 +1,25 @@
/**
* Validates the thesis title on the frontend
* @param {string} title - The raw input from the title field
* @returns {object} Validation result containing status and cleaned title
*/
export function validateThesisTitle(title) {
if (!title) {
return { isValid: false, error: 'empty' };
}
// Replicate strip_tags / trim
const cleanedTitle = title.replace(/<\/?[^>]+(>|$)/g, "").trim();
if (cleanedTitle === '') {
return { isValid: false, error: 'empty' };
}
// Replicate the emoji/pictograph rejection
const emojiRegex = /\p{Extended_Pictographic}/u;
if (emojiRegex.test(cleanedTitle)) {
return { isValid: false, error: 'invalid_characters' };
}
return { isValid: true, cleanedTitle: cleanedTitle };
}
File diff suppressed because it is too large Load Diff
@@ -1,71 +0,0 @@
import ApiLehre from "../../api/factory/lehre.js";
const options = Vue.ref([]);
const params = Vue.ref({});
let appContext = null;
export function setupContext(globalProps) {
appContext = globalProps
}
// bind and watch api params via reference
export function bindParams(paramsRef) {
Vue.watch(
paramsRef,
(newVal) => {
params.value = { ...newVal };
fetchLehreinheiten(newVal.lv_id, newVal.sem_kurzbz);
},
{ immediate: true, deep: true }
);
}
async function fetchLehreinheiten(lv_id, sem_kurzbz) {
appContext.$api.call(ApiLehre.getLeForLv(lv_id, sem_kurzbz)).then(res => {
const data = []
// TODO: could be done on server in some shared function, copied from anw extension for now
res.data?.retval?.forEach(entry => {
const existing = data.find(e => e.lehreinheit_id === entry.lehreinheit_id)
if (existing) {
// supplement info
existing.infoString += ', '
if (entry.gruppe_kurzbz !== null && entry.direktinskription == false) {
existing.infoString += entry.gruppe_kurzbz
} else {
existing.infoString += entry.kurzbzlang + '-' + entry.semester
+ (entry.verband ? entry.verband : '')
+ (entry.gruppe ? entry.gruppe : '')
}
} else {
// entries are supposed to be fetched ordered by non null gruppe_kurzbz first
// so a new entry will always start with those groups, others are appended afterwards
entry.infoString = entry.kurzbz + ' - ' + entry.lehrform_kurzbz + ' - '
if (entry.gruppe_kurzbz !== null && entry.direktinskription == false) {
entry.infoString += entry.gruppe_kurzbz
} else {
entry.infoString += entry.kurzbzlang + '-' + entry.semester
+ (entry.verband ? entry.verband : '')
+ (entry.gruppe ? entry.gruppe : '')
}
data.push(entry)
}
})
options.value = [...data]
})
}
// export the module and relevant fields via reactive
const LehreinheitenModule = Vue.reactive({
options,
optionLabel: 'infoString',
placeholder: Vue.computed(()=>appContext?.$p.t('lehre/lehreinheit')),
setupContext,
bindParams
});
export default LehreinheitenModule;
@@ -2,7 +2,6 @@ import {CoreFilterCmpt} from "../../filter/Filter.js";
import FormForm from '../../Form/Form.js';
import FormInput from '../../Form/Input.js';
import ApiGruppe from "../../../api/lehrveranstaltung/gruppe.js";
import drop from '../../../directives/drop.js';
export default{
name: "LVGruppen",
components: {
@@ -10,9 +9,6 @@ export default{
FormForm,
FormInput
},
directives: {
drop
},
props: {
lehreinheit_id: Number
},
@@ -191,48 +187,28 @@ export default{
this.reload();
})
},
onDropVerband(node, gruppe) {
this.$api.call(ApiGruppe.getGruppe(gruppe))
.then(result => result.data)
.then(result => {
let newData = {
'gid': result,
'lehreinheit_id': this.lehreinheit_id,
'lehrverband': gruppe.lehrverband,
}
this.$api.call(ApiGruppe.add(newData))
.then(result => {
this.reload()
})
});
},
},
template: `
<div v-drop:link-strict.verband="(evt, gruppe) => onDropVerband(node, gruppe)">
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
:reload=true
>
<template #search> <!--TODO (david) Slot prüfen -->
<form-input
type="autocomplete"
:suggestions="filteredGroups"
:placeholder="$p.t('lehre', 'addGroup')"
v-model="selectedGroup"
field="label"
@item-select="addGroup"
@complete="searchGroup"
></form-input>
</template>
</core-filter-cmpt>
</div>
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
:reload=true
>
<template #search> <!--TODO (david) Slot prüfen -->
<form-input
type="autocomplete"
:suggestions="filteredGroups"
:placeholder="$p.t('lehre', 'addGroup')"
v-model="selectedGroup"
field="label"
@item-select="addGroup"
@complete="searchGroup"
></form-input>
</template>
</core-filter-cmpt>
`
};
@@ -8,10 +8,9 @@ import LvTabs from "./Setup/Tabs.js";
import ApiDetails from "../../api/lehrveranstaltung/details.js";
import ApiLektor from "../../api/lehrveranstaltung/lektor.js";
import ApiGruppe from "../../api/lehrveranstaltung/gruppe.js";
import ApiStudiengangTree from "../../api/lehrveranstaltung/studiengangtree.js";
import ApiSearchbar from "../../api/factory/searchbar.js";
import AppConfig from "../AppConfig.js";
import ApiLvConfig from "../../api/lehrveranstaltung/config.js";
export default {
@@ -24,7 +23,6 @@ export default {
StvStudiensemester,
LvTable,
LvTabs,
AppConfig
},
props: {
defaultSemester: String,
@@ -34,9 +32,7 @@ export default {
stg: { type: String, required: false },
semester: { type: [Number, String], required: false, default: null },
studiensemester_kurzbz: { type: String, required: false, default: null },
emp: { type: String, required: false, default: null },
avatarUrl: String,
logoutUrl: String,
emp: { type: String, required: false, default: null }
},
provide() {
@@ -48,6 +44,7 @@ export default {
lehreinheitAnmerkungDefault: (this.config.lehreinheitAnmerkungDefault || '').replace(/\\n/g, '\n'),
lehreinheitRaumtypDefault: this.config.lehreinheitRaumtypDefault,
lehreinheitRaumtypAlternativeDefault: this.config.lehreinheitRaumtypAlternativeDefault,
permissionLehrveranstaltung: this.permissions['lehre/lehrveranstaltung'],
permissionGruppenEntfernen: this.permissions['lv-plan/gruppenentfernen'],
permissionLektorEntfernen: this.permissions['lv-plan/lektorentfernen'],
@@ -75,8 +72,6 @@ export default {
},
data() {
return {
appconfig:{},
configEndpoints: ApiLvConfig,
selected: [],
studiengang: "",
filter: {},
@@ -274,51 +269,6 @@ export default {
<span class="fa-solid fa-table-list"></span>
</button>
<core-searchbar :searchoptions="searchbaroptions" :searchfunction=searchfunction class="searchbar w-100"></core-searchbar>
<div id="nav-user" class="dropdown">
<button
id="nav-user-btn"
class="btn btn-link rounded-0 py-0"
type="button"
data-bs-toggle="dropdown"
data-bs-target="#nav-user-menu"
aria-expanded="false"
aria-controls="nav-user-menu"
>
<img
:src="avatarUrl"
:alt="$p.t('profilUpdate/profilBild')"
class="bg-light avatar rounded-circle border border-light"
/>
</button>
<ul
ref="navUserDropdown"
class="dropdown-menu dropdown-menu-dark dropdown-menu-end rounded-0 text-center m-0"
aria-labelledby="nav-user-btn"
>
<li>
<button
type="button"
class="dropdown-item"
data-bs-toggle="modal"
data-bs-target="#configModal"
>
{{ $p.t('ui/settings') }}
</button>
</li>
<li><hr class="dropdown-divider m-0"/></li>
<li>
<nav-language
item-class="dropdown-item border-left-dark"
/>
</li>
<li><hr class="dropdown-divider m-0"/></li>
<li>
<a class="dropdown-item" :href="logoutUrl">
{{ $p.t('ui/logout') }}
</a>
</li>
</ul>
</div>
</header>
<div class="container-fluid overflow-hidden">
<div class="row h-100">
@@ -335,7 +285,7 @@ export default {
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<stv-verband :preselectedKey="selectedStudiengang" :dragEnabled=true :endpoint="endpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-verband :preselectedKey="selectedStudiengang" :endpoint="endpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester v-model:studiensemester-kurzbz="selectedStudiensemester" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
</nav>
@@ -377,6 +327,5 @@ export default {
</main>
</div>
</div>
<app-config ref="config" v-model="appconfig" :endpoints="configEndpoints"></app-config>
</div>`
};
@@ -140,7 +140,7 @@ export default{
{
this.$emit('changedLektor', this.changed.mitarbeiter_uid);
}
else if (this.changed.semesterstunden || this.changed.stundensatz)
if (this.changed.semesterstunden || this.changed.stundensatz)
{
this.$emit('changedCosts');
}
@@ -183,6 +183,9 @@ export default{
},
},
created() {
this.getLektorData()
},
template: `
<core-form ref="form" @submit.prevent="updateDaten">
<div class="position-sticky top-0 z-1">

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