Merge branch 'master' into bug-77253/StudVw_Abschlusspuefung_ImproveDropDownAkadGrad

This commit is contained in:
Harald Bamberger
2026-07-31 16:42:28 +02:00
29 changed files with 210 additions and 33 deletions
@@ -259,7 +259,7 @@ class BetriebsmittelP extends FHCAPI_Controller
$kaution = $this->input->post('kaution');
if($kaution) $kaution = str_replace(',', '.', $kaution);
$ausgegebenam = $this->input->post('ausgegebenam');
$retouram = $this->input->post('retouram');
$retouram = $this->input->post('retouram') == "" ? null : $this->input->post('retouram');
// NOTE(chris): transform_kartennummer
if ($betriebsmitteltyp == 'Zutrittskarte' && $nummer)
@@ -9,9 +9,9 @@ class Detailheader extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getHeader' => ['vertrag/mitarbeiter:r'],
'getPersonAbteilung' => ['vertrag/mitarbeiter:r'],
'getLeitungOrg' => ['vertrag/mitarbeiter:r'],
'getHeader' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r'],
'getPersonAbteilung' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r'],
'getLeitungOrg' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r'],
]);
}
@@ -294,6 +294,10 @@ class Funktionen extends FHCAPI_Controller
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
if($datum_von == '')
$datum_von = null;
if($datum_bis == '')
$datum_bis = null;
$result = $this->BenutzerfunktionModel->update(
[
@@ -371,6 +371,9 @@ class Abschlusspruefung extends FHCAPI_Controller
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$datum = $formData['datum'] == '' ? null : $formData['datum'];
$sponsion = $formData['sponsion'] == '' ? null : $formData['sponsion'];
$result = $this->AbschlusspruefungModel->update(
[
'abschlusspruefung_id' => $abschlusspruefung_id
@@ -382,8 +385,8 @@ class Abschlusspruefung extends FHCAPI_Controller
'vorsitz' => $vorsitz,
'pruefungsantritt_kurzbz' => $formData['pruefungsantritt_kurzbz'],
'abschlussbeurteilung_kurzbz' => $formData['abschlussbeurteilung_kurzbz'],
'datum' => $formData['datum'],
'sponsion' => $formData['sponsion'],
'datum' => $datum,
'sponsion' => $sponsion,
'pruefer1' => $pruefer1,
'pruefer2' => $pruefer2,
'pruefer3' => $pruefer3,
@@ -170,6 +170,8 @@ class Dokumente extends FHCAPI_Controller
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$nachgereicht_am = $this->input->post('nachgereicht_am') == '' ? null : $this->input->post('nachgereicht_am');
$uid = getAuthUID();
$result = $this->AkteModel->update(
@@ -180,7 +182,7 @@ class Dokumente extends FHCAPI_Controller
'dokument_kurzbz' => $this->input->post('dokument_kurzbz'),
'anmerkung_intern' => $this->input->post('anmerkung_intern'),
'titel_intern' => $this->input->post('titel_intern'),
'nachgereicht_am' => $this->input->post('nachgereicht_am'),
'nachgereicht_am' => $nachgereicht_am,
'updateamum' => date('c'),
'updatevon' => $uid,
]
@@ -429,9 +429,12 @@ class Konto extends FHCAPI_Controller
'updateamum' => date('c'),
'updatevon' => getAuthUID()
];
foreach ($allowed as $field)
if ($this->input->post($field) !== null)
$data[$field] = $this->input->post($field);
if($data['buchungsdatum'] == '')
$data['buchungsdatum'] = null;
$result = $this->KontoModel->update($id, $data);
@@ -237,8 +237,8 @@ class Mobility extends FHCAPI_Controller
}
$formData = $this->input->post('formData');
$von = $formData['von'] ?? null;
$bis = $formData['bis'] ?? null;
$von = ($formData['von'] ?? '') ?: null;
$bis = ($formData['bis'] ?? '') ?: null;
$nation_code = $formData['nation_code'] ?? null;
$mobilitaetsprogramm_code = $formData['mobilitaetsprogramm_code'] ?? null;
$herkunftsland_code = $formData['herkunftsland_code'] ?? null;
@@ -193,7 +193,7 @@ class Prestudent extends FHCAPI_Controller
}
elseif (
$val === ''
&& in_array($prop, ['zgvnation', 'zgvmanation', 'zgvdoktornation', 'berufstaetigkeit_code', 'ausbildungcode'])
&& in_array($prop, ['zgvnation', 'zgvmanation', 'zgvdoktornation', 'berufstaetigkeit_code', 'ausbildungcode', 'zgvdatum', 'zgvmadatum'])
)
{
$val = null;
@@ -448,8 +448,10 @@ class Pruefung extends FHCAPI_Controller
}
}
$datum = $this->input->post('datum') == '' ? null : $this->input->post('datum');
if(intval($pruefung_id) > 0)
{
$result = $this->PruefungModel->update(
[
'pruefung_id' => $pruefung_id
@@ -459,7 +461,7 @@ class Pruefung extends FHCAPI_Controller
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'note' => $this->input->post('note'),
'pruefungstyp_kurzbz' => $this->input->post('pruefungstyp_kurzbz'),
'datum' => $this->input->post('datum'),
'datum' => $datum,
'anmerkung' => $this->input->post('anmerkung'),
'updatevon' => $authUID,
'updateamum' => date('c'),
@@ -472,7 +474,7 @@ class Pruefung extends FHCAPI_Controller
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'datum' => $this->input->post('datum'),
'datum' => $datum,
'pruefungstyp_kurzbz' => $this->input->post('pruefungstyp_kurzbz'),
'note' => $this->input->post('note'),
'anmerkung' => $this->input->post('anmerkung'),
@@ -520,7 +522,7 @@ class Pruefung extends FHCAPI_Controller
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'note' => $this->input->post('note'),
'uebernahmedatum' => date('c'),
'benotungsdatum' => $this->input->post('datum'),
'benotungsdatum' => $datum,
'insertamum' => date('c'),
'insertvon' => $authUID,
'punkte' => $this->input->post('punkte') ? str_replace(',', '.', $this->input->post('punkte')) : null
@@ -555,7 +557,7 @@ class Pruefung extends FHCAPI_Controller
], [
'note' => $this->input->post('note'),
'uebernahmedatum' => date('c'),
'benotungsdatum' => $this->input->post('datum'),
'benotungsdatum' => $datum,
'updateamum' => date('c'),
'updatevon' => $authUID,
'punkte' => $this->input->post('punkte') ? str_replace(',', '.', $this->input->post('punkte')) : null
@@ -296,6 +296,10 @@ class Student extends FHCAPI_Controller
$fotoval = ($val == '') ? null : str_replace('data:image/jpeg;base64,', '', $val);
$update_person[$prop] = $fotoval;
}
else if ($prop == 'gebdatum')
{
$update_person[$prop] = ($val == '') ? null : $val;
}
else
{
$update_person[$prop] = $val;
+4 -1
View File
@@ -80,10 +80,12 @@ class StundenplanLib
if (isError($semester_range))
return $semester_range;
$semester_range = getData($semester_range);
$this->_ci->addMeta('semester_range', $semester_range);
$this->sortStudienSemester($semester_range);
$function_error = $this->applyLoadUeberSemesterHaelfte($semester_range);
$this->_ci->addMeta('nach_apply_semester_range', $semester_range);
if ($function_error)
return $function_error;
@@ -784,7 +786,8 @@ class StundenplanLib
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
// gets all studiensemester from the student from start_date to end_date
$semester_range = $this->_ci->StudiensemesterModel->getByDateRange($start_date, $end_date);
//$semester_range = $this->_ci->StudiensemesterModel->getByDateRange($start_date, $end_date);
$semester_range = $this->_ci->StudiensemesterModel->getContainingOrNearestByDateRange($start_date, $end_date);
if (isError($semester_range))
return $semester_range;
@@ -122,6 +122,47 @@ class Studiensemester_model extends DB_Model
return $this->execQuery($query, array($studiensemester_kurzbz, $limit));
}
/**
* Returns the current study semester and the previous semesters of the same type (SS/WS).
*
* Example:
* - SS2026, limit 3: SS2026, SS2025, SS2024
* - WS2025, limit 2: WS2025, WS2024
*
* @param $studiensemester_kurzbz
* @param $limit
*/
public function getPreviousSameSemesterFrom($studiensemester_kurzbz, $limit = 1)
{
$qry = '
SELECT
studiensemester_kurzbz,
start,
ende
FROM
public.tbl_studiensemester
WHERE
SUBSTRING(studiensemester_kurzbz FROM 1 FOR 2) = SUBSTRING(? FROM 1 FOR 2)
AND start <= (
SELECT start
FROM public.tbl_studiensemester
WHERE studiensemester_kurzbz = ?
)
ORDER BY
start DESC
LIMIT ?
';
return $this->execQuery($qry,
[
$studiensemester_kurzbz,
$studiensemester_kurzbz,
$limit
]
);
}
/**
* getNearest
*/
@@ -188,6 +229,82 @@ class Studiensemester_model extends DB_Model
return $this->execQuery($query, array($date,$date,$date,$date));
}
/**
* Gets all Studiensemester where the range dates are contained
* or the nearest if date is not contained
* @param $from
* @param $to
* @return array|null
*/
public function getContainingOrNearestByDateRange($from, $to)
{
if (date_format(date_create($from), 'Y-m-d') > (date_format(date_create($to), 'Y-m-d')))
return success(array());
$query = <<<EOSQL
WITH semester_interval_startdate AS (
SELECT
s.*
FROM
public.tbl_studiensemester s
WHERE
{$this->db->escape($from)}::date BETWEEN s.start AND s.ende
ORDER BY
s.start DESC
LIMIT 1
),
semester_interval_enddate AS (
SELECT
s.*
FROM
public.tbl_studiensemester s
WHERE
{$this->db->escape($to)}::date BETWEEN s.start AND s.ende
ORDER BY
s.start DESC
LIMIT 1
),
nearest_semester_interval_startdate AS (
SELECT
LEAST(ABS(s.start - {$this->db->escape($from)}::date), ABS(s.ende - {$this->db->escape($from)}::date)) AS mindiff,
s.*
FROM
public.tbl_studiensemester s
WHERE
s.ende < {$this->db->escape($from)}::date
ORDER BY
1 ASC
LIMIT 1
),
nearest_semester_interval_enddate AS (
SELECT
LEAST(ABS(s.start - {$this->db->escape($to)}::date), ABS(s.ende - {$this->db->escape($to)}::date)) AS mindiff,
s.*
FROM
public.tbl_studiensemester s
WHERE
s.start > {$this->db->escape($to)}::date
ORDER BY
1 ASC
LIMIT 1
)
SELECT
COALESCE(
(SELECT studiensemester_kurzbz FROM semester_interval_startdate),
(SELECT studiensemester_kurzbz FROM nearest_semester_interval_startdate)
) AS studiensemester_kurzbz
UNION
SELECT
COALESCE(
(SELECT studiensemester_kurzbz FROM semester_interval_enddate),
(SELECT studiensemester_kurzbz FROM nearest_semester_interval_enddate)
) AS studiensemester_kurzbz
EOSQL;
return $this->execReadonlyQuery($query);
}
/**
* Gets all Studiensemester between two dates
* @param $from
@@ -260,7 +260,6 @@ class Benutzerfunktion_model extends DB_Model
return $this->execQuery($query, $parameters_array);
}
/**
* Get active Kompetenzfeldleitung bei UID.
*
@@ -296,7 +295,6 @@ class Benutzerfunktion_model extends DB_Model
return $this->execQuery($query, $parameters_array);
}
public function insertBenutzerfunktion($Json)
{
unset($Json['benutzerfunktion_id']);
@@ -433,6 +433,7 @@ export default {
:label="$p.t('wawi/ausgegebenam')"
name="ausgegebenam"
v-model="formData.ausgegebenam"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -449,6 +450,7 @@ export default {
:label="$p.t('wawi/retouram')"
name="retouram"
v-model="formData.retouram"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -551,6 +551,7 @@ export default {
container-class="mb-3 col-3"
type="DatePicker"
v-model="formData.datum_von"
model-type="yyyy-MM-dd"
name="datum_von"
:label="$p.t('ui/from') + ' *'"
auto-apply
@@ -566,6 +567,7 @@ export default {
container-class="mb-3 col-3"
type="DatePicker"
v-model="formData.datum_bis"
model-type="yyyy-MM-dd"
name="datum_bis"
:label="$p.t('global/bis')"
auto-apply
@@ -470,6 +470,7 @@ export default {
:label="$p.t('admission', 'anmeldundRtAm')"
type="DatePicker"
v-model="formData.anmeldedatum"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -293,6 +293,7 @@ export default {
:label="$p.t('person', 'geburtsdatum')"
type="DatePicker"
v-model="data.gebdatum"
model-type="yyyy-MM-dd"
name="gebdatum"
:clearable="false"
no-today
@@ -103,6 +103,7 @@ export default {
<form-input
type="DatePicker"
v-model="formData.nachgereicht_am"
model-type="yyyy-MM-dd"
name="nachgereicht_am"
:label="$p.t('dokumente/nachreichungAm')"
auto-apply
@@ -27,7 +27,7 @@ export default {
return {
filter: false,
studiengang_kz: false,
counterdate: new Date()
counterdate: luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate()
};
},
computed: {
@@ -259,6 +259,7 @@ export default {
<form-input
type="DatePicker"
v-model="counterdate"
model-type="yyyy-MM-dd"
input-group
:enable-time-picker="false"
text-input
@@ -79,6 +79,7 @@ export default {
<form-input
type="DatePicker"
v-model="data.buchungsdatum"
model-type="yyyy-MM-dd"
name="buchungsdatum"
:label="$p.t('konto/buchungsdatum')"
:enable-time-picker="false"
@@ -88,7 +88,7 @@ export default {
this.data = {
buchungstyp_kurzbz: '',
betrag: '-0.00',
buchungsdatum: new Date(),
buchungsdatum: luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate(),
buchungstext: '',
mahnspanne: 30,
studiensemester_kurzbz: this.currentSemester,
@@ -154,6 +154,7 @@ export default {
<form-input
type="DatePicker"
v-model="data.buchungsdatum"
model-type="yyyy-MM-dd"
name="buchungsdatum"
:label="$p.t('konto/buchungsdatum')"
:enable-time-picker="false"
@@ -467,6 +467,7 @@ export default {
:label="$p.t('ui', 'von')"
type="DatePicker"
v-model="formData.von"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -501,6 +502,7 @@ export default {
:label="$p.t('global', 'bis')"
type="DatePicker"
v-model="formData.bis"
model-type="yyyy-MM-dd"
auto-apply
:enable-time-picker="false"
text-input
@@ -325,6 +325,7 @@ export default {
:label="$p.t('infocenter', 'zgvDatum')"
type="DatePicker"
v-model="data.zgvdatum"
model-type="yyyy-MM-dd"
name="zgvdatum"
no-today
auto-apply
@@ -389,6 +390,7 @@ export default {
:label="$p.t('lehre', 'zgvMasterDatum')"
type="DatePicker"
v-model="data.zgvmadatum"
model-type="yyyy-MM-dd"
name="zgvmadatum"
no-today
auto-apply
@@ -454,6 +456,7 @@ export default {
:label="$p.t('lehre', 'zgvDoktorDatum')"
type="DatePicker"
v-model="data.zgvdoktordatum"
model-type="yyyy-MM-dd"
name="zgvdoktordatum"
no-today
auto-apply
@@ -276,6 +276,7 @@ export default{
container-class="mb-3"
type="DatePicker"
v-model="formData.datum"
model-type="yyyy-MM-dd"
name="datum"
:label="$p.t('global/datum')"
auto-apply
@@ -291,6 +292,7 @@ export default{
container-class="mb-3"
type="DatePicker"
v-model="formData.bestaetigtam"
model-type="yyyy-MM-dd"
name="bestaetigtam"
:label="$p.t('lehre/bestaetigt_am')"
auto-apply
@@ -306,13 +308,14 @@ export default{
container-class="mb-3"
type="DatePicker"
v-model="formData.bewerbung_abgeschicktamum"
model-type="yyyy-MM-dd HH:mm:ss"
name="bewerbung_abgeschicktamum"
:label="$p.t('lehre/bewerbung_abgeschickt_am')"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
:enable-time-picker="true"
format="dd.MM.yyyy HH:mm:ss"
text-input
preview-format="dd.MM.yyyy"
preview-format="dd.MM.yyyy HH:mm:ss"
:teleport="true"
:disabled="bisLocked || !hasPrestudentstatusPermission"
>
@@ -285,6 +285,7 @@ export default {
uid="stv-list-new-gebdatum"
name="gebdatum"
v-model="formDataPerson['gebdatum']"
model-type="yyyy-MM-dd"
:disabled="!!person"
@update:model-value="loadSuggestions"
text-input
+17 -3
View File
@@ -4,6 +4,7 @@ import FormInput from '../Form/Input.js';
import ApiUdf from '../../api/udf.js';
export default {
name: 'CoreUdfCmpt',
components: {
CoreFetchCmpt,
FormInput
@@ -26,7 +27,8 @@ export default {
// The values as associative array
modelValue: Object,
// Show only fields with a name that exists in the filter
filter: [String, Array]
filter: [String, Array],
readonly: true
},
data() {
return {
@@ -35,6 +37,13 @@ export default {
};
},
computed: {
isPkValid() {
// If the provided pk is _not_ valid
return !Object.keys(this.pk).every(key => !this.pk[key]);
},
getReadonly() {
return this.readonly;
},
filterArray() {
if (!this.filter || Array.isArray(this.filter))
return this.filter;
@@ -61,6 +70,10 @@ export default {
},
watch: {
pk(n, o) {
// If the provided pk is _not_ valid
if (Object.keys(n).every(key => !n[key])) return;
if (!this.$refs.fetch)
return; // NOTE(chris): no initial load yet
@@ -124,6 +137,7 @@ export default {
template: `
<div class="core-udf row">
<core-fetch-cmpt
v-if="isPkValid"
ref="fetch"
:api-function="loadF"
:api-function-parameters="{ ciModel, pk }"
@@ -138,7 +152,7 @@ export default {
:type="field.type"
:multiple="field.multiple"
:title="field.description"
:disabled="field.disabled"
:disabled="field.disabled || getReadonly"
:clearable="field.clearable"
:auto-apply="field.autoApply"
:enable-time-picker="field.enableTimePicker"
@@ -152,4 +166,4 @@ export default {
</template>
</core-fetch-cmpt>
</div>`
}
}
+16 -8
View File
@@ -218,15 +218,23 @@ export const CoreFilterCmpt = {
},
columnsForFilter() {
if (!this.filteredColumns || !this.datasetMetadata) return [];
const localizedColumnTitles = this.tabulator.getLang().columns;
const isTabulatorLocalized = !!this.$props.tabulatorOptions.locale;
const filterTitles = this.filteredColumns.reduce((a, c) => {
a[c.field] =
isTabulatorLocalized && localizedColumnTitles[c.field]
? localizedColumnTitles[c.field]
: c.title;
return a;
}, {});
let filterTitles;
if (isTabulatorLocalized) {
const localizedColumnTitles = this.tabulator.getLang().columns;
filterTitles = this.filteredColumns.reduce((a, c) => {
a[c.field] = localizedColumnTitles[c.field];
return a;
}, {});
} else {
filterTitles = this.filteredColumns.reduce((a, c) => {
a[c.field] = c.title;
return a;
}, {});
}
return this.datasetMetadata.map((el) => ({
...el,
...{ title: filterTitles[el.name] },
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB