standort filter in raumsuche; fix the ort/standort queries so _DUMMY rooms actually get filtered out as intended

This commit is contained in:
Johann Hoffmann
2026-10-01 17:21:55 +02:00
parent f3d031ab16
commit 9b137452d9
4 changed files with 122 additions and 10 deletions
@@ -36,7 +36,8 @@ class Ort extends FHCAPI_Controller
'ContentID' => self::PERM_LOGGED,
'getOrtKurzbzContent' => self::PERM_LOGGED,
'getRooms' => self::PERM_LOGGED,
'getTypes' => self::PERM_LOGGED
'getTypes' => self::PERM_LOGGED,
'getStandorte' => self::PERM_LOGGED
]);
$this->load->model('ressource/Ort_model', 'OrtModel');
@@ -56,6 +57,7 @@ class Ort extends FHCAPI_Controller
$this->form_validation->set_rules('datum','Datum','required');
$this->form_validation->set_rules('von','Uhrzeit Von','required|regex_match[/^[0-9]{2}:[0-9]{2}$/]');
$this->form_validation->set_rules('bis','Uhrzeit Bis','required|regex_match[/^[0-9]{2}:[0-9]{2}$/]');
$this->form_validation->set_rules('standort_id','Standort','integer');
if($this->form_validation->run() == FALSE) {
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
@@ -65,6 +67,7 @@ class Ort extends FHCAPI_Controller
$bis = $this->input->get('bis', TRUE);
$typ = $this->input->get('typ', TRUE);
$personenanzahl = $this->input->get('personenanzahl', TRUE);
$standort_id = $this->input->get('standort_id', TRUE);
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
@@ -75,14 +78,22 @@ class Ort extends FHCAPI_Controller
$bisStunde = getData($this->StundeModel->getStundeForTime($bis))[0]->stunde;
$params = array();
$qry = "SELECT DISTINCT tbl_ort.*
// a leading _ in ort_kurzbz or planbezeichnung marks a dummy room
$qry = "SELECT DISTINCT tbl_ort.*, tbl_standort.bezeichnung AS standort
FROM public.tbl_ort JOIN public.tbl_ortraumtyp USING(ort_kurzbz)
WHERE aktiv AND lehre AND ort_kurzbz NOT LIKE '\\\\_%'";
LEFT JOIN public.tbl_standort USING(standort_id)
WHERE aktiv AND lehre AND left(ort_kurzbz, 1) <> '_'
AND (planbezeichnung IS NULL OR left(planbezeichnung, 1) <> '_')";
if($typ) {
$params[] = $typ;
$qry.= "AND raumtyp_kurzbz = ?";
}
if($standort_id) {
$params[] = $standort_id;
$qry.= " AND standort_id = ?";
}
if(!$isMitarbeiter) { // students are only allowed to get a subset defined by config
$qry.= ' AND raumtyp_kurzbz IN ?';
$params[] = $this->config->item('roomtypes_student');
@@ -127,6 +138,22 @@ class Ort extends FHCAPI_Controller
$this->terminateWithSuccess(getData($result));
}
/**
* Standorte of the rooms that pass the base filter of getRooms
*/
public function getStandorte()
{
$qry = "SELECT DISTINCT standort_id, tbl_standort.bezeichnung
FROM public.tbl_ort JOIN public.tbl_standort USING(standort_id)
WHERE aktiv AND lehre AND left(ort_kurzbz, 1) <> '_'
AND (planbezeichnung IS NULL OR left(planbezeichnung, 1) <> '_')
ORDER BY tbl_standort.bezeichnung";
$result = $this->OrtModel->execReadOnlyQuery($qry);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
/**
* Gets a JSON body via HTTP POST and provides the parameters
*/
+9 -2
View File
@@ -23,11 +23,11 @@ export default {
params: { ort_kurzbz: ort_kurbz }
};
},
getRooms(datum, von, bis, typ, personenanzahl = 0) {
getRooms(datum, von, bis, typ, personenanzahl = 0, standort_id = null) {
return {
method: 'get',
url: '/api/frontend/v1/Ort/getRooms',
params: { datum, von, bis, typ, personenanzahl }
params: { datum, von, bis, typ, personenanzahl, standort_id }
};
},
getRoomTypes() {
@@ -36,5 +36,12 @@ export default {
url: '/api/frontend/v1/Ort/getTypes',
params: { }
};
},
getStandorte() {
return {
method: 'get',
url: '/api/frontend/v1/Ort/getStandorte',
params: { }
};
}
};
@@ -23,6 +23,8 @@ export const Raumsuche = {
},
anzahl: 1,
selectedType: null,
standorte: null,
standort_id: null,
datum: new Date(),
von: Vue.ref({
hours: new Date().getHours(),
@@ -55,6 +57,7 @@ export const Raumsuche = {
{title: Vue.computed(() => this.$p.t('rauminfo/raum_kurzbz')), field: 'ort_kurzbz', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('global/bezeichnung')), field: 'bezeichnung', widthGrow: 2},
{title: Vue.computed(() => this.$p.t('rauminfo/raumnummer')), field: 'nummer', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/standort')), field: 'standort', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/personcap')), field: 'personen', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/rauminfo')),
field: 'linkInfo', formatter: this.linkFormatter, widthGrow: 1},
@@ -115,6 +118,7 @@ export const Raumsuche = {
ort_kurzbz: room.ort_kurzbz,
bezeichnung: room.bezeichnung.replace('&amp;', '&'),
nummer: room.planbezeichnung,
standort: room.standort,
personen: room.max_person,
linkInfo: room.content_id ? this.roomInfoLink(room) : null,
linkRes: this.roomPlanLink(room)
@@ -135,8 +139,14 @@ export const Raumsuche = {
this.roomtypes = res?.data ?? []
})
},
loadStandorte() {
this.$api.call(ApiOrt.getStandorte())
.then(res => {
this.standorte = res?.data ?? []
})
},
loadRooms() {
this.$api.call(ApiOrt.getRooms(this.datum.toISOString(), this.getTimeString(this.von), this.getTimeString(this.bis), this.selectedType?.raumtyp_kurzbz ?? '', this.anzahl))
this.$api.call(ApiOrt.getRooms(this.datum.toISOString(), this.getTimeString(this.von), this.getTimeString(this.bis), this.selectedType?.raumtyp_kurzbz ?? '', this.anzahl, this.standort_id))
.then(res => {
if(res?.data?.retval) this.setupData(res.data.retval)
})
@@ -167,6 +177,7 @@ export const Raumsuche = {
await this.tableBuiltPromise
this.loadRoomTypes()
this.loadStandorte()
this.loadRooms()
const tableID = this.tabulatorUuid ? ('-' + this.tabulatorUuid) : ''
@@ -235,23 +246,30 @@ export const Raumsuche = {
</VueDatePicker>
</div>
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-3">
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-2">
<select ref="raumtyp" id="raumtypSelect" v-model="selectedType" class="form-select"
:aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setRoute($event.target.value)">
:aria-label="$p.t('rauminfo/raumtyp')" @change="setRoute($event.target.value)">
<option :key="defaultType" selected :value="defaultType">{{defaultType.beschreibung}}</option>
<option v-for="typ in roomtypes" :key="typ" :value="typ">{{typ.beschreibung}}</option>
</select>
</div>
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-2">
<select id="standortSelect" v-model="standort_id" class="form-select" :aria-label="$p.t('rauminfo/standort')">
<option :value="null">{{ $p.t('rauminfo/alleStandorte') }}</option>
<option v-for="standort in standorte" :key="standort.standort_id" :value="standort.standort_id">{{ standort.bezeichnung }}</option>
</select>
</div>
<div :class="{'pb-2': isMobile}" class="col-12 col-lg-3">
<div :class="{'pb-2': isMobile}" class="col-12 col-lg">
<InputNumber v-model="anzahl"
:prefix="$p.t('rauminfo/minCapacity') + ': '"
inputId="anzahlInput" :min="1" :max="1000"
:style="{'width': '100%'}"
/>
</div>
<div class="col-12 col-lg-2">
<div class="col-12 col-lg-auto">
<button class="btn btn-primary border-0" @click="search">{{ $p.t('rauminfo/roomSearch') }} <i class="fa fa-magnifying-glass"></i></button>
</div>
</div>
+60
View File
@@ -21670,6 +21670,66 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'rauminfo',
'phrase' => 'standort',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Standort",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "Site",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'rauminfo',
'phrase' => 'alleStandorte',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Alle Standorte",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "All Sites",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'rauminfo',
'phrase' => 'raumtyp',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Raumtyp",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "Room Type",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',