From 85bdd3a68679f1a8ac25bc3038542d34c50da265 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 28 Sep 2026 18:40:07 +0200 Subject: [PATCH] Ferien: added import from government csv --- application/config/ferienimport.php | 5 + .../api/frontend/v1/education/Ferien.php | 112 +++++++++++++++++- .../ferienverwaltung/ferienverwaltung.js | 12 +- .../Ferienverwaltung/Ferienverwaltung.js | 93 ++++++++------- system/dbupdate_3.4/71405_ferienzeiten.php | 14 +++ 5 files changed, 192 insertions(+), 44 deletions(-) create mode 100644 application/config/ferienimport.php diff --git a/application/config/ferienimport.php b/application/config/ferienimport.php new file mode 100644 index 000000000..cd91792fd --- /dev/null +++ b/application/config/ferienimport.php @@ -0,0 +1,5 @@ + self::DEFAULT_BERECHTIGUNG.':r', 'insert' => self::DEFAULT_BERECHTIGUNG.':w', 'update' => self::DEFAULT_BERECHTIGUNG.':w', - 'delete' => self::DEFAULT_BERECHTIGUNG.':w' + 'delete' => self::DEFAULT_BERECHTIGUNG.':w', + 'importFerien' => self::DEFAULT_BERECHTIGUNG.':w' ]); // Load models @@ -50,6 +52,7 @@ class Ferien extends FHCAPI_Controller $this->load->library('PermissionLib'); + $this->load->config('ferienimport'); // Load language phrases $this->loadPhrases([ @@ -301,6 +304,85 @@ class Ferien extends FHCAPI_Controller $this->terminateWithSuccess(); } + /** + * + * @param + * @return object success or error + */ + public function importFerien() + { + $filterVonDatum = $this->input->get('filterVonDatum'); + $filterBisDatum = $this->input->get('filterBisDatum'); + + if (isset($filterVonDatum) && !is_valid_date($filterVonDatum)) + return $this->terminateWithError($this->p->t('ui', 'error_invalid_date'), self::ERROR_TYPE_GENERAL); + + if (isset($filterBisDatum) && !is_valid_date($filterBisDatum)) + return $this->terminateWithError($this->p->t('ui', 'error_invalid_date'), self::ERROR_TYPE_GENERAL); + + $noImported = 0; + + $data = file_get_contents($this->config->item('ferien_import_link')); + + if (!$data) return $this->terminateWithError($this->p->t('ferien', 'csvNichtGelesen'), self::ERROR_TYPE_GENERAL); + + $rows = explode("\n", $data); + $headers = []; + $ferien = []; + $headerRow = true; + + foreach($rows as $row) + { + /* + * Wenn escape auf etwas anderes als eine leere Zeichenkette ("") gesetzt wird, kann dies zu einer CSV-Datei führen, + * die nicht mit » RFC 4180 konform ist oder die den Umlauf durch die PHP-CSV-Funktionen nicht übersteht. + * Der Standardwert für escape ist "\\", weshalb empfohlen wird, diesen Parameter explizit auf eine leere Zeichenkette zu setzen. + */ + + $fe = str_getcsv($row, ",", "\"", ''); + if ($headerRow) + { + $headers = $fe; + } + else + { + $importedFerien = []; + foreach ($fe as $idx => $value) + { + if (!isset($headers[$idx])) continue; + $importedFerien[$headers[$idx]] = $value; + } + + $ferien = $this->_mapImportFerien($importedFerien); + if ( + !$ferien + || ($filterVonDatum != null && new DateTime($filterVonDatum) > new DateTime($ferien['bisdatum'])) + || ($filterBisDatum != null && new DateTime($filterBisDatum) < new DateTime($ferien['vondatum'])) + ) continue; + + $result = $this->FerienModel->loadWhere([ + 'vondatum' => $ferien['vondatum'], + 'bisdatum' => $ferien['bisdatum'], + 'bezeichnung' => $ferien['bezeichnung'], + 'oe_kurzbz' => null + ]); + + if (isError($result)) return $this->terminateWithError($this->p->t('ferien', 'fehlerBeimLaden'), self::ERROR_TYPE_GENERAL); + + // add Ferien if there have been + if (!hasData($result)) + { + $result = $this->FerienModel->insert($ferien); + if (isError($result)) return $this->terminateWithError($this->p->t('ferien', 'fehlerBeimHinzufuegen'), self::ERROR_TYPE_GENERAL); + $noImported++; + } + } + + $headerRow = false; + } + $this->terminateWithSuccess($noImported); + } + /** * Validate ferien post input. */ @@ -368,4 +450,32 @@ class Ferien extends FHCAPI_Controller return $data; } + + /** + * + * @param + * @return object success or error + */ + private function _mapImportFerien($importedFerien) + { + $mappings = [ + 'vondatum' => 'DATUM', + 'bisdatum' => 'DATUM', + 'bezeichnung' => 'TEXT' + ]; + + $ferien = [ + 'ferientyp_kurzbz' => self::FERIENTYP_KURZBZ, + 'insertamum' => date('c'), + 'insertvon' => getAuthUID() + ]; + + foreach ($mappings as $key => $name) + { + if (!isset($importedFerien[$name])) return null; + $ferien[$key] = $importedFerien[$name]; + } + + return $ferien; + } } diff --git a/public/js/api/factory/ferienverwaltung/ferienverwaltung.js b/public/js/api/factory/ferienverwaltung/ferienverwaltung.js index 09c9efa2f..202e8b394 100644 --- a/public/js/api/factory/ferienverwaltung/ferienverwaltung.js +++ b/public/js/api/factory/ferienverwaltung/ferienverwaltung.js @@ -75,5 +75,15 @@ export default { url: 'api/frontend/v1/education/ferien/delete', params: { ferien_id } }; - } + }, + importFerien(filterVonDatum, filterBisDatum) { + return { + method: 'get', + url: 'api/frontend/v1/education/ferien/importFerien', + params: { + filterVonDatum, + filterBisDatum + } + }; + }, }; \ No newline at end of file diff --git a/public/js/components/Ferienverwaltung/Ferienverwaltung.js b/public/js/components/Ferienverwaltung/Ferienverwaltung.js index ab6fef7a4..30901b553 100644 --- a/public/js/components/Ferienverwaltung/Ferienverwaltung.js +++ b/public/js/components/Ferienverwaltung/Ferienverwaltung.js @@ -156,47 +156,33 @@ export default { event: 'tableBuilt', handler: async () => { - //await this.$p.loadCategory(['ferien', 'ui']); await this.$p.loadCategory(['global', 'ferien']); - let cm = this.$refs.table.tabulator.columnManager; + const setHeader = (field, text) => { + const col = this.$refs.table.tabulator.getColumn(field); + if (!col) return; - cm.getColumnByField('ferien_id').component.updateDefinition({ - title: this.$p.t('ferien', 'ferienId'), - }); - cm.getColumnByField('vondatum').component.updateDefinition({ - title: this.$p.t('ferien', 'vondatum'), - }); - cm.getColumnByField('bisdatum').component.updateDefinition({ - title: this.$p.t('ferien', 'bisdatum'), - }); - cm.getColumnByField('bezeichnung').component.updateDefinition({ - title: this.$p.t('global', 'bezeichnung'), - }); - cm.getColumnByField('oe_kurzbz').component.updateDefinition({ - title: this.$p.t('ferien', 'oeKurzbezeichnung'), - }); - cm.getColumnByField('oe_bezeichnung').component.updateDefinition({ - title: this.$p.t('ferien', 'oeBezeichnung'), - }); - cm.getColumnByField('studienplan_bezeichnung').component.updateDefinition({ - title: this.$p.t('ferien', 'studienplanBezeichnung'), - }); - cm.getColumnByField('ferientyp_kurzbz').component.updateDefinition({ - title: this.$p.t('ferien', 'ferientypKurzbz'), - }); - cm.getColumnByField('mitarbeiterrelevant').component.updateDefinition({ - title: this.$p.t('ferien', 'mitarbeiterrelevant'), - }); - cm.getColumnByField('studierendenrelevant').component.updateDefinition({ - title: this.$p.t('ferien', 'studierendenrelevant'), - }); - cm.getColumnByField('lehre').component.updateDefinition({ - title: this.$p.t('ferien', 'lehrePlanbar'), - }); - cm.getColumnByField('actions').component.updateDefinition({ - title: this.$p.t('global', 'aktionen') - }); + const el = col.getElement(); + if (!el || !el.querySelector) return; + + const titleEl = el.querySelector('.tabulator-col-title'); + if (titleEl) { + titleEl.textContent = text; + } + }; + + setHeader('ferien_id', this.$p.t('ferien', 'ferienId')); + setHeader('vondatum', this.$p.t('ferien', 'vondatum')); + setHeader('bisdatum', this.$p.t('ferien', 'bisdatum')); + setHeader('bezeichnung', this.$p.t('global', 'bezeichnung')); + setHeader('oe_kurzbz', this.$p.t('ferien', 'oeKurzbezeichnung')); + setHeader('oe_bezeichnung', this.$p.t('ferien', 'oeBezeichnung')); + setHeader('studienplan_bezeichnung', this.$p.t('global', 'studienplanBezeichnung')); + setHeader('ferientyp_kurzbz', this.$p.t('ferien', 'ferientypKurzbz')); + setHeader('mitarbeiterrelevant', this.$p.t('ferien', 'mitarbeiterrelevant')); + setHeader('studierendenrelevant', this.$p.t('ferien', 'studierendenrelevant')); + setHeader('lehre', this.$p.t('ferien', 'lehrePlanbar')); + setHeader('actions', this.$p.t('global', 'aktionen')); } } ] @@ -210,6 +196,19 @@ export default { }, actionNew() { this.$refs.modal.open(); + }, + importFerien() { + this.$api + .call(ApiFerienverwaltung.importFerien(this.filterVonDatum, this.filterBisDatum)) + .then(result => { + this.$fhcAlert.alertSuccess(this.$p.t('ferien', 'importiert') + ": " + result.data); + this.reload(); + } + ) + .catch(error => { + if (error) + this.$fhcAlert.handleSystemError(error); + }); } }, created() { @@ -235,8 +234,8 @@ export default {
-
-
+
+
-
+
-
+
+
+ +
diff --git a/system/dbupdate_3.4/71405_ferienzeiten.php b/system/dbupdate_3.4/71405_ferienzeiten.php index 60ad5f528..d20011a8d 100644 --- a/system/dbupdate_3.4/71405_ferienzeiten.php +++ b/system/dbupdate_3.4/71405_ferienzeiten.php @@ -204,3 +204,17 @@ if (!$result = @$db->db_query('SELECT 0 FROM lehre.tbl_ferientyp WHERE 0 = 1')) else echo '
Granted privileges to vilesci on lehre.tbl_ferientyp'; } + +// Add UNIQUE constraint on fehlercode and fehler_kurzbz on system.tbl_fehler +if ($result = @$db->db_query("SELECT conname FROM pg_constraint WHERE conname = 'uk_tbl_ferien_vondatum_bisdatum_oe_bezeichnung'")) +{ + if ($db->db_num_rows($result) == 0) + { + $qry = "ALTER TABLE lehre.tbl_ferien ADD CONSTRAINT uk_tbl_ferien_vondatum_bisdatum_oe_bezeichnung UNIQUE (vondatum, bisdatum, oe_kurzbz, bezeichnung);"; + + if (!$db->db_query($qry)) + echo 'lehre.tbl_ferien '.$db->db_last_error().'
'; + else + echo '
added UNIQUE constraint on "vondatum", "bisdatum" and "bezeichnung" for lehre.tbl_ferien
'; + } +}