diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index d52016943..b08af66bb 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -33,6 +33,7 @@ class Config extends FHCAPI_Controller { // TODO(chris): permissions parent::__construct([ + 'filter' => ['admin:r', 'assistenz:r'], 'student' => ['admin:r', 'assistenz:r'], 'students' => ['admin:r', 'assistenz:r'] ]); @@ -52,6 +53,158 @@ class Config extends FHCAPI_Controller $this->load->config('stv'); } + /** + * Get the config for the student filters + * + * @return void + */ + public function filter() + { + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + + $this->load->model('crm/Buchungstyp_model', 'BuchungstypModel'); + + $this->BuchungstypModel->addOrder('beschreibung'); + + $result = $this->BuchungstypModel->load(); + + $buchungstyp_kurzbz = $this->getDataOrTerminateWithError($result); + $buchungstyp_kurzbz_plus_all = array_merge([[ + 'buchungstyp_kurzbz' => 'all', + 'beschreibung' => $this->p->t('stv', 'konto_all_types') + ]], $buchungstyp_kurzbz); + + $this->load->model('crm/Statusgrund_model', 'StatusgrundModel'); + + $result = $this->StatusgrundModel->getAktiveGruende(); + + $statusgruende = $this->getDataOrTerminateWithError($result); + + $result = []; + + $result[] = [ + 'id' => 'filter_konto_count_0', + 'label' => $this->p->t('stv', 'filter_konto_count_0'), + 'type' => 'konto', + 'fixed' => [ + 'missing' => true, + 'usestdsem' => true + ], + 'dynamic' => [ + 'buchungstyp_kurzbz' => [ + 'type' => 'select', + 'values' => $buchungstyp_kurzbz, + 'value_key' => 'buchungstyp_kurzbz', + 'label_key' => 'beschreibung' + ] + ] + ]; + $result[] = [ + 'id' => 'filter_konto_missing_counter', + 'label' => $this->p->t('stv', 'filter_konto_missing_counter'), + 'type' => 'konto_counter', + 'dynamic' => [ + 'buchungstyp_kurzbz' => [ + 'type' => 'select', + 'values' => $buchungstyp_kurzbz_plus_all, + 'value_key' => 'buchungstyp_kurzbz', + 'label_key' => 'beschreibung' + ], + 'samestg' => [ + 'type' => 'bool', + 'label' => $this->p->t('stv', 'filter_konto_samestg'), + 'default' => $this->variablelib->getVar('kontofilterstg') == 'true' + ] + ] + ]; + $result[] = [ + 'id' => 'filter_documents', + 'label' => $this->p->t('stv', 'filter_documents'), + 'type' => 'documents' + ]; + $result[] = [ + 'id' => 'filter_konto_missing_counter_past', + 'label' => $this->p->t('stv', 'filter_konto_missing_counter_past'), + 'type' => 'konto_counter', + 'fixed' => [ + 'past' => true + ], + 'dynamic' => [ + 'buchungstyp_kurzbz' => [ + 'type' => 'select', + 'values' => $buchungstyp_kurzbz_plus_all, + 'value_key' => 'buchungstyp_kurzbz', + 'label_key' => 'beschreibung' + ], + 'samestg' => [ + 'type' => 'bool', + 'label' => $this->p->t('stv', 'filter_konto_samestg'), + 'default' => $this->variablelib->getVar('kontofilterstg') == 'true' + ] + ] + ]; + $result[] = [ + 'id' => 'filter_konto_missing_studiengebuehr', + 'label' => $this->p->t('stv', 'filter_konto_missing_studiengebuehr'), + 'type' => 'konto', + 'fixed' => [ + 'missing' => true, + 'usestdsem' => true + ], + 'dynamic' => [ + 'buchungstyp_kurzbz' => [ + 'type' => 'select', + 'values' => $buchungstyp_kurzbz, + 'value_key' => 'buchungstyp_kurzbz', + 'label_key' => 'beschreibung' + ] + ] + ]; + $result[] = [ + 'id' => 'filter_konto_studiengebuehrerhoeht', + 'label' => $this->p->t('stv', 'filter_konto_studiengebuehrerhoeht'), + 'type' => 'konto', + 'fixed' => [ + 'usestdsem' => true + ], + 'dynamic' => [ + 'buchungstyp_kurzbz' => [ + 'type' => 'select', + 'values' => $buchungstyp_kurzbz, + 'value_key' => 'buchungstyp_kurzbz', + 'label_key' => 'beschreibung' + ] + ] + ]; + $result[] = [ + 'id' => 'filter_zgv_without_date', + 'label' => $this->p->t('stv', 'filter_zgv_without_date'), + 'type' => 'zgv' + ]; + $result[] = [ + 'id' => 'filter_statusgrund', + 'label' => $this->p->t('stv', 'filter_statusgrund'), + 'type' => 'statusgrund', + 'fixed' => [ + 'usestdsem' => true + ], + 'dynamic' => [ + 'statusgrund_id' => [ + 'type' => 'select', + 'values' => $statusgruende, + 'value_key' => 'statusgrund_id', + 'label_key' => 'bezeichnung' + ] + ] + ]; + + Events::trigger('stv_conf_filter', function & () use (&$result) { + return $result; + }); + + $this->terminateWithSuccess($result); + } + public function student() { $result = []; diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index 9de0c29b1..49fecbfcd 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -44,7 +44,6 @@ class Students extends FHCAPI_Controller } // Load Libraries - $this->load->library('VariableLib', ['uid' => getAuthUID()]); $this->load->library('PhrasesLib'); $this->loadPhrases( array( @@ -854,40 +853,20 @@ class Students extends FHCAPI_Controller */ protected function addFilter($studiensemester_kurzbz) { - $filter = json_decode($this->input->get('filter'), true); + $filter = $this->input->post('filter'); + if (!is_array($filter)) { - $this->addMeta('addfilter', 'invalid filter: ' . $this->input->get('filter')); + $this->addMeta('addfilter', 'invalid filter: ' . json_encode($this->input->post('filter'))); return; } - if (isset($filter['konto_count_0'])) { - $bt = $this->PrestudentModel->escape($filter['konto_count_0']); - $stdsem = $this->PrestudentModel->escape($studiensemester_kurzbz); - - $this->PrestudentModel->db->where('( - SELECT count(*) - FROM public.tbl_konto - WHERE person_id=tbl_prestudent.person_id - AND buchungstyp_kurzbz=' . $bt . ' - AND studiensemester_kurzbz=' . $stdsem . ' - ) =', 0); - $this->PrestudentModel->db->where('get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) !=', 'Incoming'); - } - if (isset($filter['konto_missing_counter'])) { - $bt = $this->PrestudentModel->escape($filter['konto_missing_counter']); - $stg = ''; - if ($this->variablelib->getVar('kontofilterstg') == 'true') - $stg = ' AND studiengang_kz=tbl_prestudent.studiengang_kz'; - - $bt = $bt == 'alle' ? '' : ' AND buchungstyp_kurzbz=' . $bt; - - $this->PrestudentModel->db->where('( - SELECT sum(betrag) - FROM public.tbl_konto - WHERE person_id=tbl_prestudent.person_id' . - $bt . - $stg . ' - ) !=', 0); + foreach ($filter as $item) { + if (isset($item['usestdsem']) && $item['usestdsem']) + $item['studiensemester_kurzbz'] = $studiensemester_kurzbz; + if (!$this->PrestudentModel->addFilter($item)) { + $this->addMeta('addfilter', 'invalid filter: ' . json_encode($item)); + return; + } } } } diff --git a/application/models/crm/Prestudent_model.php b/application/models/crm/Prestudent_model.php index ff56c3268..ad5c3e141 100644 --- a/application/models/crm/Prestudent_model.php +++ b/application/models/crm/Prestudent_model.php @@ -1,5 +1,7 @@ execQuery($query, array($person_id)); } + + /** + * Adds a filter to the query builder + * + * @param array $filter + * @return boolean + */ + public function addFilter($filter) + { + if (!isset($filter['type'])) + return false; + + switch ($filter['type']) { + case 'konto': + $bt = ''; + $stdsem = ''; + $comp = '!='; + + if (isset($filter['buchungstyp_kurzbz']) && $filter['buchungstyp_kurzbz'] != 'all') + $bt = ' AND buchungstyp_kurzbz=' . $this->escape($filter['buchungstyp_kurzbz']); + + if (isset($filter['studiensemester_kurzbz'])) + $stdsem = ' AND studiensemester_kurzbz=' . $this->escape($filter['studiensemester_kurzbz']); + + if (isset($filter['missing']) && $filter['missing']) { + $comp = '='; + $this->db->where('get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) !=', 'Incoming'); + } + + $this->db->where('( + SELECT count(*) + FROM public.tbl_konto + WHERE person_id=tbl_prestudent.person_id + ' . $bt . ' + ' . $stdsem . ' + ) ' . $comp, 0); + break; + + case 'konto_counter': + $bt = ''; + $samestg = ''; + $past = ''; + + if (isset($filter['buchungstyp_kurzbz']) && $filter['buchungstyp_kurzbz'] != 'all') + $bt = ' AND buchungstyp_kurzbz = ' . $this->escape($filter['buchungstyp_kurzbz']); + + if (isset($filter['samestg']) && $filter['samestg']) + $samestg = ' AND studiengang_kz = tbl_prestudent.studiengang_kz'; + + if (isset($filter['past']) && $filter['past']) + $past = ' AND buchungsdatum < NOW()'; + + $this->db->where('( + SELECT sum(betrag) + FROM public.tbl_konto + WHERE person_id = tbl_prestudent.person_id + ' . $bt . ' + ' . $samestg . ' + ' . $past . ' + ) !=', 0); + break; + + case 'zgv': + $this->db + ->group_start() + ->group_start() + ->where('zgv_code IS NOT NULL') + ->where('zgvdatum IS NULL') + ->group_end() + ->or_group_start() + ->where('zgvmas_code IS NOT NULL') + ->where('zgvmadatum IS NULL') + ->group_end() + ->or_group_start() + ->where('zgvdoktor_code IS NOT NULL') + ->where('zgvdoktordatum IS NULL') + ->group_end() + ->group_end(); + break; + + case 'documents': + $this->db->where('( + SELECT count(*) + FROM public.tbl_dokumentstudiengang + WHERE dokument_kurzbz NOT IN ( + SELECT dokument_kurzbz + FROM tbl_dokumentprestudent + WHERE prestudent_id=tbl_prestudent.prestudent_id + ) + AND studiengang_kz=tbl_prestudent.studiengang_kz + ) !=', 0); + break; + + case 'statusgrund': + if (!isset($filter['statusgrund_id'])) + return false; + + if (isset($filter['studiensemester_kurzbz'])) + $stdsem = ' AND studiensemester_kurzbz=' . $this->escape($filter['studiensemester_kurzbz']); + + $this->db->where('( + SELECT count(*) + FROM public.tbl_prestudentstatus + WHERE prestudent_id = tbl_prestudent.prestudent_id + AND statusgrund_id = ' . $this->escape($filter['statusgrund_id']) . ' + ' . $stdsem . ' + ) !=', 0); + break; + } + + Events::trigger('prestudent_add_filter', $filter); + + return true; + } } diff --git a/public/js/api/factory/stv/app.js b/public/js/api/factory/stv/app.js index 6fcf46700..d75207c26 100644 --- a/public/js/api/factory/stv/app.js +++ b/public/js/api/factory/stv/app.js @@ -16,6 +16,12 @@ */ export default { + configFilter() { + return { + method: 'get', + url: 'api/frontend/v1/stv/config/filter' + }; + }, configStudent() { return { method: 'get', diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index 67cada523..56f3cb0d5 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -1,12 +1,14 @@ import {CoreFilterCmpt} from "../../filter/Filter.js"; import ListNew from './List/New.js'; +import ListFilter from './List/Filter.js'; export default { name: "ListPrestudents", components: { CoreFilterCmpt, - ListNew + ListNew, + ListFilter }, inject: { 'lists': { @@ -119,7 +121,7 @@ export default { { return Promise.resolve({ data: []}); } - return this.$api.call({url, params}); + return this.$api.call({method: 'post', url, params}); }, ajaxResponse: (url, params, response) => { return response?.data; @@ -157,8 +159,7 @@ export default { ], focusObj: null, // TODO(chris): this should be in the filter component lastSelected: null, - filterKontoCount0: undefined, - filterKontoMissingCounter: undefined, + filter: [], count: 0, filteredcount: 0, selectedcount: 0, @@ -194,6 +195,10 @@ export default { } } }, + updateFilter(filter) { + this.filter = filter; + this.updateUrl(); + }, updateUrl(endpoint, first) { this.lastSelected = first ? undefined : this.selected; @@ -214,14 +219,9 @@ export default { encodeURIComponent(this.currentSemester) ); - const params = {}, filter = {}; - if (this.filterKontoCount0) - filter.konto_count_0 = this.filterKontoCount0; - if (this.filterKontoMissingCounter) - filter.konto_missing_counter = this.filterKontoMissingCounter; - - if (filter.konto_count_0 || filter.konto_missing_counter) - params.filter = filter; + const params = {}; + if (this.filter.length) + params.filter = this.filter; if (!this.$refs.table.tableBuilt) { if (!this.$refs.table.tabulator) { @@ -311,9 +311,9 @@ export default { }, // TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component // TODO(chris): filter component column chooser has no accessibilty features - template: ` + template: /* html */`
-
+
-
- - - -
-
- - - -
+
diff --git a/public/js/components/Stv/Studentenverwaltung/List/Filter.js b/public/js/components/Stv/Studentenverwaltung/List/Filter.js new file mode 100644 index 000000000..0570d8ef2 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/List/Filter.js @@ -0,0 +1,102 @@ +import FilterItem from './Filter/Item.js'; + +import ApiStvApp from '../../../../api/factory/stv/app.js'; + +export default { + name: "ListPrestudentsFilter", + components: { + FilterItem + }, + emits: [ + 'change' + ], + data() { + return { + filters: [], + filterConfig: [// TODO(chris): get from BE! + { + name: 'stv/konto_filter_count_0', + type: 'konto', + fixed: { + missing: true, + usestdsem: true + }, + dynamic: { + buchungstyp_kurzbz: { + type: 'select', + values: { + test1: 'Test1', + test2: 'Test2' + } + } + } + }, + { + name: 'stv/konto_filter_missing_counter', + type: 'konto_counter', + dynamic: { + buchungstyp_kurzbz: { + type: 'select', + values: { + test1: 'Test1', + test2: 'Test2' + } + }, + samestg: { + type: 'bool', + label: 'stv/konto', + default: true + } + } + } + ] + } + }, + computed: { + cleanFilters() { + return this.filters.filter(filter => { + if (!filter.type) + return false; + if (Object.values(filter).some(v => v === undefined)) + return false; + return true; + }); + } + }, + watch: { + cleanFilters(n) { + this.$emit('change', n); + } + }, + methods: { + add() { + this.filters.push({}); + }, + remove(index) { + this.filters.splice(index, 1); + } + }, + created() { + this.$api + .call(ApiStvApp.configFilter()) + .then(result => { + this.filterConfig = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + }, + template: /* html */` +
+ + +
` +}; diff --git a/public/js/components/Stv/Studentenverwaltung/List/Filter/Item.js b/public/js/components/Stv/Studentenverwaltung/List/Filter/Item.js new file mode 100644 index 000000000..bfde531f8 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/List/Filter/Item.js @@ -0,0 +1,113 @@ +export default { + name: "FilterItem", + props: { + modelValue: Object, + filterConfig: Array + }, + emits: [ + 'update:modelValue', + 'remove' + ], + data() { + return { + //type: this.modelValue.type + }; + }, + computed: { + value: { + get() { + return this.modelValue; + }, + set(value) { + this.$emit('update:modelValue', value); + } + }, + filterid: { + get() { + return this.modelValue.filterid + }, + set(filterid) { + const config = this.filterConfig.find(config => config.id == filterid); + const dynamic = Object.fromEntries( + Object.keys(config.dynamic || {}).map(key => { + return [ + key, + config.dynamic[key].default + ]; + }) + ); + this.value = { + filterid, + type: config.type, + ...(config.fixed || {}), + ...dynamic + }; + } + }, + currentConfig() { + return this.filterConfig.find(config => config.id == this.filterid); + } + }, + methods: { + update() { + this.value = this.value; + } + }, + template: /* html */` +
+ + + + +
` +}; diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index ceb265b14..b31885783 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -2037,6 +2037,26 @@ $phrases = array( ), //*************************** CORE/filter + array( + 'app' => 'core', + 'category' => 'filter', + 'phrase' => 'filter', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Filter', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Filter', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'filter', @@ -38640,6 +38660,206 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_for', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Liste Filtern auf', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Filter list for', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_konto_count_0', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'nicht belastet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'not charged', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_konto_missing_counter', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'fehlende Gegenbuchungen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'missing offsetting entries', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_documents', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'fehlende Dokumente', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'missing documents', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_konto_missing_counter_past', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'überfällige Buchungen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'overdue payments', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_konto_missing_studiengebuehr', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'nicht gebuchte Studiengebühr', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'outstanding tuition fee', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_konto_studiengebuehrerhoeht', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'erhöhten Studienbeitrag', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'higher tuition fee', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_zgv_without_date', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'ZGV eingetragen ohne Datum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'ZGV set without date', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_statusgrund', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Statusgrund', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Status reason', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'filter_konto_samestg', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nur im aktuellen Studiengang', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Only in the selected study program', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // konto array( 'app' => 'core', @@ -38741,46 +38961,6 @@ array( ) ) ), - array( - 'app' => 'core', - 'category' => 'stv', - 'phrase' => 'konto_filter_count_0', - 'insertvon' => 'system', - 'phrases' => array( - array( - 'sprache' => 'German', - 'text' => 'Liste filtern auf nicht belastet:', - 'description' => '', - 'insertvon' => 'system' - ), - array( - 'sprache' => 'English', - 'text' => 'Filter list to not charged:', - 'description' => '', - 'insertvon' => 'system' - ) - ) - ), - array( - 'app' => 'core', - 'category' => 'stv', - 'phrase' => 'konto_filter_missing_counter', - 'insertvon' => 'system', - 'phrases' => array( - array( - 'sprache' => 'German', - 'text' => 'Liste filtern auf fehlende Gegenbuchungen:', - 'description' => '', - 'insertvon' => 'system' - ), - array( - 'sprache' => 'English', - 'text' => 'Filter the list for missing offsetting entries:', - 'description' => '', - 'insertvon' => 'system' - ) - ) - ), array( 'app' => 'core', 'category' => 'stv',