diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index ac1e132fb..bcea93fb5 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -83,6 +83,10 @@ class Config extends FHCAPI_Controller 'title' => 'Status', 'component' => './Stv/Studentenverwaltung/Details/MultiStatus.js' ]; + $result['documents'] = [ + 'title' => $this->p->t('stv', 'tab_dokumente'), + 'component' => './Stv/Studentenverwaltung/Details/Dokumente.js' + ]; $result['banking'] = [ 'title' => $this->p->t('stv', 'tab_banking'), 'component' => './Stv/Studentenverwaltung/Details/Konto.js', diff --git a/application/controllers/api/frontend/v1/stv/Dokumente.php b/application/controllers/api/frontend/v1/stv/Dokumente.php new file mode 100644 index 000000000..2923f7204 --- /dev/null +++ b/application/controllers/api/frontend/v1/stv/Dokumente.php @@ -0,0 +1,553 @@ + ['admin:r', 'assistenz:r'], + 'getDocumentsAccepted' => ['admin:r', 'assistenz:r'], + 'deleteZuordnung' => ['admin:rw', 'assistenz:rw'], + 'createZuordnung' => ['admin:rw', 'assistenz:rw'], + 'loadAkte' => ['admin:rw', 'assistenz:rw'], + 'deleteAkte' => ['admin:rw', 'assistenz:rw'], + 'updateAkte' => ['admin:rw', 'assistenz:rw'], + 'getDoktypen' => ['admin:r', 'assistenz:r'], + 'uploadDokument' => ['admin:rw', 'assistenz:rw'], + 'download' => ['admin:rw', 'assistenz:rw'], + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + $this->load->library('form_validation'); + + // Load language phrases + $this->loadPhrases([ + 'ui', + 'dokumente' + ]); + + // Load models + $this->load->model('crm/Akte_model', 'AkteModel'); + $this->load->model('crm/Dokument_model', 'DokumentModel'); + $this->load->model('crm/Dokumentprestudent_model', 'DokumentprestudentModel'); + + //TODO(Manu) check additional Berechtigungen + //TODO(Manu) check if using dokument lib instead of dokument model? + } + + public function getDocumentsUnaccepted($prestudent_id, $studiengang_kz) + { + if(!$prestudent_id) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if (!is_numeric($prestudent_id)) + $this->terminateWithError($this->p->t('ui', 'error_valueNotNumeric', ['value' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if(!$studiengang_kz) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Studiengang_kz']), self::ERROR_TYPE_GENERAL); + + $person_id = $this->_getPersonId($prestudent_id); + $result = $this->DokumentModel->getUnacceptedDocuments($prestudent_id, $person_id); + + $dataAkteUnaccepted = $this->getDataOrTerminateWithError($result); + $resultMd = $this->_getMissingDocuments($studiengang_kz, $prestudent_id); + + $data = $this->_mergeDocuments($dataAkteUnaccepted, $resultMd); + + $this->terminateWithSuccess($data); + } + + public function getDocumentsAccepted($prestudent_id, $studiengang_kz) + { + if(!$prestudent_id) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if (!is_numeric($prestudent_id)) + $this->terminateWithError($this->p->t('ui', 'error_valueNotNumeric', ['value' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if(!$studiengang_kz) + $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); + + foreach($docNames as $doc) + { + $result = $this->AkteModel->getAktenFAS($person_id, $doc, $studiengang_kz, $prestudent_id, true); + + if (isError($result)) + { + return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL); + } + if (hasData($result)) + { + $data = getData($result); + array_push($arrayAccepted, current($data)); + } + } + + //Merge with Data of Akte + $newDataMap = []; + //helper, lookupArray + foreach ($arrayAccepted as $item) { + $newDataMap[$item->dokument_kurzbz] = $item; + } + + //add Data of Akte to PrestudentData + foreach ($resultPreDoc as $obj) { + if (isset($newDataMap[$obj->dokument_kurzbz])) { + $obj->akte_id = $newDataMap[$obj->dokument_kurzbz]->akte_id; + $obj->hochgeladenamum = $newDataMap[$obj->dokument_kurzbz]->hochgeladenamum; + $obj->titel_intern = $newDataMap[$obj->dokument_kurzbz]->titel_intern; + $obj->nachgereicht = $newDataMap[$obj->dokument_kurzbz]->nachgereicht; + $obj->akzeptiertamum = $newDataMap[$obj->dokument_kurzbz]->akzeptiertamum; + $obj->dms_id = $newDataMap[$obj->dokument_kurzbz]->dms_id; + $obj->infotext = $newDataMap[$obj->dokument_kurzbz]->infotext; + $obj->anmerkung_intern = $newDataMap[$obj->dokument_kurzbz]->anmerkung_intern; + $obj->nachgereicht_am = $newDataMap[$obj->dokument_kurzbz]->nachgereicht_am; + $obj->vorhanden = $newDataMap[$obj->dokument_kurzbz]->vorhanden; + } + } + $this->terminateWithSuccess($resultPreDoc); + } + + public function deleteZuordnung($prestudent_id, $dokument_kurzbz) + { + if(!$prestudent_id) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if (!is_numeric($prestudent_id)) + $this->terminateWithError($this->p->t('ui', 'error_valueNotNumeric', ['value' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if(!$dokument_kurzbz) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Dokument_kurzbz']), self::ERROR_TYPE_GENERAL); + + $result = $this->DokumentprestudentModel->delete( + [ + 'prestudent_id' => $prestudent_id, + 'dokument_kurzbz' => $dokument_kurzbz + ] + ); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function loadAkte($akte_id) + { + if (!$akte_id) + $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Akte ID']), self::ERROR_TYPE_GENERAL); + + $this->AkteModel->addSelect('public.tbl_akte.*'); + $this->AkteModel->addSelect("CONCAT(public.tbl_person.vorname, ' ' , public.tbl_person.nachname) AS namePerson"); + $this->AkteModel->addJoin('public.tbl_person', 'person_id'); + $result = $this->AkteModel->loadWhere( + [ + 'akte_id' => $akte_id, + ] + ); + + $data = $this->getDataOrTerminateWithError($result); + $data = current($data); + $this->terminateWithSuccess($data); + } + + public function updateAkte() + { + $this->form_validation->set_rules('akte_id', 'Akte ID', 'required', [ + 'required' => $this->p->t('dokumente', 'err_updateNotAllowed') + ]); + + $this->form_validation->set_rules('dokument_kurzbz', 'Dokumenttyp', 'required', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Dokumenttyp']) + ]); + + $this->form_validation->set_rules('nachreichung_am', 'Nachreichung am', 'is_valid_date', [ + 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'Nachreichung am']) + ]); + + if ($this->form_validation->run() == false) + { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + + $uid = getAuthUID(); + + $result = $this->AkteModel->update( + [ + 'akte_id' => $this->input->post('akte_id'), + ], + [ + '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'), + 'updateamum' => date('c'), + 'updatevon' => $uid, + ] + ); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess(current($data)); + } + + public function createZuordnung($prestudent_id, $dokument_kurzbz) + { + if (!$prestudent_id) + $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Prestudent ID']), self::ERROR_TYPE_GENERAL); + + if(!$dokument_kurzbz) + $this->terminateWithError($this->p->t('ui', 'errorMissingValue', ['value' => 'Dokument_kurzbz']), self::ERROR_TYPE_GENERAL); + + $uid = getAuthUid(); + + $result = $this->DokumentprestudentModel->insert( + [ + 'prestudent_id' => $prestudent_id, + 'dokument_kurzbz' => $dokument_kurzbz, + 'mitarbeiter_uid' => $uid, + 'datum' => date('c'), + 'insertamum' => date('c'), + 'insertvon' => $uid, + ] + ); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function deleteAkte($akte_id) + { + if (!$akte_id) + $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Akte ID']), self::ERROR_TYPE_GENERAL); + + $result = $this->AkteModel->load($akte_id); + $dataAkte = $this->getDataOrTerminateWithError($result); + + $logdata_akte = var_export($dataAkte, true); + + $dms_id = current($dataAkte)->dms_id; + $nachgereicht = current($dataAkte)->nachgereicht; + $inhalt = current($dataAkte)->inhalt; + $inhaltVorhanden = $inhalt != ''; + $uid = getAuthUid(); + + $this->db->trans_start(); + + if($dms_id) + { + $this->load->model('content/Dms_model', 'DmsModel'); + $result = $this->DmsModel->load($dms_id); + $data = $this->getDataOrTerminateWithError($result); + + $logdata_dms = (array)$data; + $logdata_dms = "Logdata: " . var_export($logdata_dms, true); + + //delete from dmsLib + $this->load->library('DmsLib'); + $person_id = current($dataAkte)->person_id; + $result = $this->dmslib->delete($person_id, $dms_id); + $this->getDataOrTerminateWithError($result); + + //LOGGING Dms ID + $this->load->model('system/Log_model', 'LogModel'); + $result = $this->LogModel->insert([ + 'executetime' => date('c'), + 'mitarbeiter_uid' => $uid, + 'beschreibung' => "Löschen der DMS_ID ". $dms_id, + 'sql' => $logdata_dms + ]); + $this->getDataOrTerminateWithError($result); + + //delete akte + $result = $this->AkteModel->delete( + [ + 'akte_id' => $akte_id + ] + ); + $data = $this->getDataOrTerminateWithError($result); + + //Logging Deletion Akte + $result = $this->LogModel->insert([ + 'executetime' => date('c'), + 'mitarbeiter_uid' => $uid, + 'beschreibung' => "Löschen der Akte ". $akte_id, + 'sql' => "DELETE FROM public.tbl_akte WHERE akte_id=" .$akte_id. " LogData: ". $logdata_akte + ]); + $this->getDataOrTerminateWithError($result); + $this->db->trans_complete(); + $this->terminateWithSuccess($data); + } + elseif (!!$dms_id || ($nachgereicht && !$inhaltVorhanden)) + { + $result = $this->AkteModel->delete( + [ + 'akte_id' => $akte_id + ] + ); + $data = $this->getDataOrTerminateWithError($result); + + $result = $this->LogModel->insert([ + 'executetime' => date('c'), + 'mitarbeiter_uid' => $uid, + 'beschreibung' => "Löschen der Akte ". $akte_id, + 'sql' => "DELETE FROM public.tbl_akte WHERE akte_id=" .$akte_id. " LogData: ". $logdata_akte + ]); + $this->getDataOrTerminateWithError($result); + + $this->db->trans_complete(); + $this->terminateWithSuccess($data); + } + else + $this->terminateWithError($this->p->t('dokumente', 'err_deleteDokHere'), self::ERROR_TYPE_GENERAL); + } + + public function uploadDokument() + { + $this->load->library('DmsLib'); + $prestudent_id = $this->input->post('prestudent_id'); + $anmerkung_intern = $this->input->post('anmerkung_intern'); + $titel_intern = $this->input->post('titel_intern'); + $dokument_kurzbz = $this->input->post('dokument_kurzbz'); + + $this->form_validation->set_rules('prestudent_id', 'Prestudent_id', 'required', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Prestudent ID']) + ]); + + $this->form_validation->set_rules('dokument_kurzbz', 'Dokumenttyp', 'required', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Dokumenttyp']) + ]); + + //validation if attachment was added + $this->form_validation->set_rules('anhang', 'Attachment', 'callback_file_check'); + + if ($this->form_validation->run() == false) + { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + + $this->db->trans_start(); + $uid = getAuthUID(); + + $dms = array( + 'kategorie_kurzbz' => 'Akte', + 'version' => 0, + 'name' => $_FILES['anhang']['name'], + 'mimetype' => $_FILES['anhang']['type'], + 'insertamum' => date('c'), + 'insertvon' => $uid + ); + + $result = $this->dmslib->upload($dms, 'anhang', array("jpg", "png", "pdf")); + + if (isError($result)) + { + return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $dms_id = $result->retval['dms_id']; + + $person_id = $this->_getPersonId($prestudent_id); + + $result = $this->DokumentModel->load($dokument_kurzbz); + $data = $this->getDataOrTerminateWithError($result); + + $bezeichnung = current($data)->bezeichnung; + + //save entry in akte + if($dms_id) + { + $result = $this->AkteModel->insert([ + 'person_id' => $person_id, + 'dms_id' => $dms_id, + 'dokument_kurzbz' => $dokument_kurzbz, + 'mimetype' => $_FILES['anhang']['type'], + 'insertamum' => date('c'), + 'erstelltam' => date('c'), + 'insertvon' => $uid, + 'anmerkung_intern' => $anmerkung_intern, + 'titel_intern' => $titel_intern, + 'bezeichnung' => $bezeichnung, + 'titel' => $_FILES['anhang']['name'] + ]); + + $data = $this->getDataOrTerminateWithError($result); + $this->db->trans_complete(); + $this->terminateWithSuccess($data); + } + $this->db->trans_complete(); + $this->terminateWithSuccess($data); + } + + public function getDoktypen() + { + $this->DokumentModel->addSelect('dokument_kurzbz'); + $this->DokumentModel->addSelect('bezeichnung'); + $this->DokumentModel->addOrder('dokument_kurzbz', 'ASC'); + $result = $this->DokumentModel->load(); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function download() + { + //TODO(Manu) check filetype, Decoding + $akte_id = $this->input->get('akte_id'); + + if(!$akte_id) + $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Akte ID']), self::ERROR_TYPE_GENERAL); + + if (!is_numeric($akte_id)) + $this->terminateWithError($this->p->t('ui', 'error_valueNotNumeric', ['value' => 'Akte ID']), self::ERROR_TYPE_GENERAL); + + + $result = $this->AkteModel->load($akte_id); + + if (!hasData($result)) $this->terminateWithError('Akte not found'); + + $data = getData($result)[0]; + + header('Content-Description: File Transfer'); + header('Content-Type: '. $data->mimetype); + header('Content-Disposition: attachment; filename="'.$data->titel.'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + if (isset($data->inhalt) && !empty($data->inhalt)) { + $decodedContent = base64_decode($data->inhalt, true); + + if ($decodedContent === false) { + die('Error: Base64-Dekodierung failed.'); + } + echo $decodedContent; + } + die(); + } + + private function _getMissingDocuments($studiengang_kz, $prestudent_id) + { + $result = $this->DokumentModel->getMissingDocuments($studiengang_kz, $prestudent_id); + + $data = $this->getDataOrTerminateWithError($result); + + return $data; + } + + private function _getUnacceptedDocuments($prestudent_id) + { + $person_id = $this->_getPersonId($prestudent_id); + $result = $this->DokumentModel->getUnacceptedDocuments($prestudent_id, $person_id); + + $data = $this->getDataOrTerminateWithError($result); + + return $data; + } + + /** + * helper function for merging objects + * sorts object after merging according to dokument_kurzbz + * @param $original object of documents of akte + * @param object $toMerge documents to merge (of dokumentprestudent, dokumentstudiengang) + * @return Array mergedObject + */ + private function _mergeDocuments($original, $toMerge) + { + $existingKurzbez = []; + foreach ($original as $doc) { + $existingKurzbez[$doc->dokument_kurzbz] = true; + } + + foreach ($toMerge as $doc) { + if (!isset($existingKurzbez[$doc->dokument_kurzbz])) { + $original[] = $doc; + $existingKurzbez[$doc->dokument_kurzbz] = true; + } + else + { + foreach($original as $docOriginal) + { + if ($docOriginal->dokument_kurzbz == $doc->dokument_kurzbz) + { + $docOriginal->pflicht = $doc->pflicht; + $docOriginal->onlinebewerbung = $doc->onlinebewerbung; + } + } + } + } + + usort($original, function ($a, $b) { + return strcmp($a->dokument_kurzbz, $b->dokument_kurzbz); + }); + + return $original; + } + + private function _getDocumentsOfAkte($person_id) + { + $result = $this->AkteModel->getAktenFAS($person_id); + + $data = $this->getDataOrTerminateWithError($result); + + return $data; + } + + private function _getPrestudentDokumente($prestudent_id) + { + $result = $this->DokumentprestudentModel->getPrestudentDokumente($prestudent_id); + + $data = $this->getDataOrTerminateWithError($result); + + return $data; + } + + private function _getPersonId($prestudent_id) + { + $this->load->model('crm/Prestudent_model', 'PrestudentModel'); + $result = $this->PrestudentModel->loadWhere( + ['prestudent_id' => $prestudent_id] + ); + + $data = $this->getDataOrTerminateWithError($result); + $person = current($data); + + return $person->person_id; + } + + public function file_check($str) + { + if (isset($_FILES['anhang']) && $_FILES['anhang']['size'] > 0) + { + $allowed_mime_types = ['image/jpeg', 'image/png', 'application/pdf']; + $mime = mime_content_type($_FILES['anhang']['tmp_name']); + + if (in_array($mime, $allowed_mime_types)) + { + return true; + } else + { + $this->form_validation->set_message('file_check', $this->p->t('dokumente', 'error_fileType')); + return false; + } + } + else + { + $this->form_validation->set_message('file_check', $this->p->t('dokumente', 'error_fileMissing')); + return false; + } + } +} diff --git a/application/models/crm/Akte_model.php b/application/models/crm/Akte_model.php index 2fa69d5b0..601019d3a 100644 --- a/application/models/crm/Akte_model.php +++ b/application/models/crm/Akte_model.php @@ -92,7 +92,7 @@ class Akte_model extends DB_Model a.anmerkung, a.nachgereicht, a.nachgereicht_am, - CASE WHEN MAX(dp.dokument_kurzbz) IS NOT NULL THEN TRUE ELSE FALSE END AS accepted + CASE WHEN MAX(dp.dokument_kurzbz) IS NOT NULL THEN TRUE ELSE FALSE END AS accepted, FROM public.tbl_akte a INNER JOIN public.tbl_prestudent p USING(person_id) LEFT JOIN public.tbl_dokumentprestudent dp USING(prestudent_id, dokument_kurzbz) @@ -111,6 +111,61 @@ class Akte_model extends DB_Model return $this->execQuery($query, $parametersArray); } + /** + * getAktenAccepted FAS + */ + public function getAktenFAS($person_id, $dokument_kurzbz = null, $stg_kz = null, $prestudent_id = null, $returnInhalt = false) + { + $query = 'SELECT + a.akte_id, + a.bezeichnung, + a.dokument_kurzbz, + a.titel_intern, + a.anmerkung_intern, + a.insertamum as hochgeladenamum, + a.updatevon, a.insertvon, a.uid, + a.dms_id, a.anmerkung as infotext, + a.nachgereicht, + CASE + WHEN inhalt IS NOT NULL OR a.dms_id IS NOT NULL + THEN true + ELSE false + END AS vorhanden, + a.nachgereicht_am, + ausstellungsnation, formal_geprueft_amum, archiv, + signiert, stud_selfservice, akzeptiertamum, inhalt + FROM public.tbl_akte a + WHERE a.person_id = ?'; + + $parametersArray = array($person_id); + + if (!isEmptyString($dokument_kurzbz)) + { + $query .= " AND dokument_kurzbz = ? + AND dokument_kurzbz NOT IN ('Zeugnis','DiplSupp','Bescheid')"; + array_push($parametersArray, $dokument_kurzbz); + } + + if($stg_kz != null && $prestudent_id != null) + { + $query.= " AND dokument_kurzbz not in ( + SELECT dokument_kurzbz + FROM public.tbl_dokument + JOIN public.tbl_dokumentstudiengang USING(dokument_kurzbz) + WHERE studiengang_kz= ? + AND dokument_kurzbz NOT IN( + SELECT dokument_kurzbz FROM public.tbl_dokumentprestudent + JOIN public.tbl_dokument USING(dokument_kurzbz) + WHERE prestudent_id=?))"; + array_push($parametersArray, $stg_kz); + array_push($parametersArray, $prestudent_id); + } + + $query .= ' ORDER BY erstelltam'; + + return $this->execQuery($query, $parametersArray); + } + /** * getAktenAcceptedDms */ diff --git a/application/models/crm/Dokument_model.php b/application/models/crm/Dokument_model.php index 7ef2be716..8008202c1 100644 --- a/application/models/crm/Dokument_model.php +++ b/application/models/crm/Dokument_model.php @@ -11,4 +11,91 @@ class Dokument_model extends DB_Model $this->dbTable = 'public.tbl_dokument'; $this->pk = 'dokument_kurzbz'; } + + /** + * Loads all missing Documents of a Studiengang + * a Prestudent has not submitted + * @param integer studiengang_kz + * @param integer prestudent_id + * @param boolean archivdokumente + * Default: true. + * If false, documents that are archivable (tbl_vorlage.archivierbar e.g. certificate, notice, ...) not retrieved + * @return Array of Documents || error + */ + public function getMissingDocuments($studiengang_kz, $prestudent_id = null, $archivdokumente = false, $person_id = null) + { + $parametersArray = array($studiengang_kz); + + $qry = "SELECT + tbl_dokument.* , + tbl_dokumentstudiengang.* + FROM public.tbl_dokument + JOIN public.tbl_dokumentstudiengang USING(dokument_kurzbz) + LEFT JOIN public.tbl_vorlage ON (tbl_dokument.dokument_kurzbz = tbl_vorlage.vorlage_kurzbz) + WHERE studiengang_kz = ? "; + + if($prestudent_id) + { + array_push($parametersArray, $prestudent_id); + $qry.=" AND tbl_dokument.dokument_kurzbz NOT IN ( + SELECT dokument_kurzbz FROM public.tbl_dokumentprestudent WHERE prestudent_id= ?)"; + } + + if(!$archivdokumente) + { + $qry.=" AND (tbl_vorlage.archivierbar = FALSE OR tbl_vorlage.archivierbar IS NULL)"; + } + + $qry.=" ORDER BY tbl_dokument.dokument_kurzbz;"; + + return $this->execQuery($qry, $parametersArray); + } + + public function getUnacceptedDocuments($prestudent_id, $person_id) + { + $parametersArray = array($person_id, $prestudent_id); + + $qry = " SELECT + a.akte_id, + a.bezeichnung, + a.dokument_kurzbz, + a.titel_intern, + a.anmerkung_intern, + a.insertamum as hochgeladenamum, + a.updatevon, + a.insertvon, + a.uid, + a.dms_id, + a.anmerkung as infotext, + a.nachgereicht, + CASE + WHEN inhalt IS NOT NULL + OR a.dms_id IS NOT NULL THEN true + ELSE false + END AS vorhanden, + a.nachgereicht_am, + ausstellungsnation, + formal_geprueft_amum, + archiv, + signiert, + stud_selfservice, + akzeptiertamum, + inhalt + FROM + public.tbl_akte a + WHERE + a.person_id = ? + AND a.dokument_kurzbz NOT IN ( + SELECT + dokument_kurzbz + FROM + public.tbl_dokumentprestudent + WHERE + prestudent_id = ? + ) + AND a.dokument_kurzbz NOT IN ('Zeugnis','DiplSupp','Bescheid') + ORDER BY a.dokument_kurzbz;"; + + return $this->execQuery($qry, $parametersArray); + } } diff --git a/application/models/crm/Dokumentprestudent_model.php b/application/models/crm/Dokumentprestudent_model.php index 0a6669359..2415ee483 100644 --- a/application/models/crm/Dokumentprestudent_model.php +++ b/application/models/crm/Dokumentprestudent_model.php @@ -69,4 +69,39 @@ class Dokumentprestudent_model extends DB_Model return $result; } + + /** + * Loads all Documents of Prestudent, already submitted + * @param integer prestudent_id + * @param boolean archivdokumente Default true. if false, archivable Documents (tbl_vorlage.archivierbar zB Zeugnis, Bescheid, ...) not retrieved + * @return Array of Documents || error + */ + public function getPrestudentDokumente($prestudent_id, $archivdokumente = true) + { + $parametersArray = array($prestudent_id); + + $qry = "SELECT + d.bezeichnung, + d.dokument_kurzbz, + dp.datum as Docdatum, + dp.mitarbeiter_uid as DocMitarbeiter_uid, + dp.insertamum as Docinsertamum, + dp.prestudent_id, + CONCAT(p.vorname, ' ', p.nachname) as insertvonma + FROM + public.tbl_dokumentprestudent dp + JOIN public.tbl_dokument d USING(dokument_kurzbz) + LEFT JOIN public.tbl_vorlage v ON (d.dokument_kurzbz = v.vorlage_kurzbz) + LEFT JOIN public.tbl_benutzer bn ON (bn.uid = dp.mitarbeiter_uid) + LEFT JOIN public.tbl_person p ON (p.person_id = bn.person_id) + WHERE + prestudent_id = ?"; + + if(!$archivdokumente) + { + $qry.=" AND (v.archivierbar = FALSE OR v.archivierbar IS NULL)"; + } + + return $this->execQuery($qry, $parametersArray); + } } diff --git a/public/js/api/factory/stv/documents.js b/public/js/api/factory/stv/documents.js new file mode 100644 index 000000000..1898023cd --- /dev/null +++ b/public/js/api/factory/stv/documents.js @@ -0,0 +1,76 @@ +/** + * 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 . + */ + +export default { + getDocumentsUnaccepted(params) { + return { + method: 'get', + url: 'api/frontend/v1/stv/dokumente/getDocumentsUnaccepted/' + params.id + '/' + params.studiengang_kz + }; + }, + getDocumentsAccepted(params) { + return { + method: 'get', + url: 'api/frontend/v1/stv/dokumente/getDocumentsAccepted/' + params.id + '/' + params.studiengang_kz + }; + }, + deleteZuordnung(params){ + return { + method: 'post', + url: 'api/frontend/v1/stv/dokumente/deleteZuordnung/' + params.prestudent_id + '/' + params.dokument_kurzbz + }; + }, + createZuordnung(params){ + return { + method: 'post', + url: 'api/frontend/v1/stv/dokumente/createZuordnung/' + params.prestudent_id + '/' + params.dokument_kurzbz + }; + }, + loadAkte(akte_id){ + return { + method: 'get', + url: 'api/frontend/v1/stv/dokumente/loadAkte/' + akte_id + }; + }, + getDoktypen(){ + return { + method: 'get', + url: 'api/frontend/v1/stv/dokumente/getDoktypen/' + }; + }, + updateFile(akte_id, params){ + return { + method: 'post', + url: 'api/frontend/v1/stv/dokumente/updateAkte/' + akte_id, + params + }; + }, + deleteFile(akte_id){ + console.log("in deleteFile " + akte_id); + return { + method: 'post', + url: 'api/frontend/v1/stv/dokumente/deleteAkte/' + akte_id, + }; + }, + uploadFile(prestudent_id, params){ + return { + method: 'post', + url: 'api/frontend/v1/stv/dokumente/uploadDokument/' + prestudent_id, + params + }; + }, +} \ No newline at end of file diff --git a/public/js/api/stv.js b/public/js/api/stv.js index 63e53f4fb..6120a5828 100644 --- a/public/js/api/stv.js +++ b/public/js/api/stv.js @@ -12,6 +12,7 @@ import abschlusspruefung from './stv/abschlusspruefung.js'; import grades from './stv/grades.js'; import mobility from './stv/mobility.js'; import archiv from './stv/archiv.js'; +import documents from './stv/documents.js'; export default { verband, @@ -28,6 +29,7 @@ export default { grades, mobility, archiv, + documents, configStudent() { return this.$fhcApi.get('api/frontend/v1/stv/config/student'); }, diff --git a/public/js/api/stv/documents.js b/public/js/api/stv/documents.js new file mode 100644 index 000000000..9934520fc --- /dev/null +++ b/public/js/api/stv/documents.js @@ -0,0 +1,33 @@ +export default { + getDocumentsUnaccepted(url, config, params) { + return this.$fhcApi.get('api/frontend/v1/stv/dokumente/getDocumentsUnaccepted/' + params.id + '/' + params.studiengang_kz); + }, + getDocumentsAccepted(url, config, params) { + return this.$fhcApi.get('api/frontend/v1/stv/dokumente/getDocumentsAccepted/' + params.id + '/' + params.studiengang_kz); + }, + deleteZuordnung(params){ + return this.$fhcApi.post('api/frontend/v1/stv/dokumente/deleteZuordnung/' + params.prestudent_id + '/' + params.dokument_kurzbz); + }, + createZuordnung(params){ + return this.$fhcApi.post('api/frontend/v1/stv/dokumente/createZuordnung/' + + params.prestudent_id + '/' + + params.dokument_kurzbz); + }, + loadAkte(akte_id){ + return this.$fhcApi.get('api/frontend/v1/stv/dokumente/loadAkte/' + akte_id); + }, + getDoktypen(){ + return this.$fhcApi.get('api/frontend/v1/stv/dokumente/getDoktypen/'); + }, + updateFile(akte_id, data){ + return this.$fhcApi.post('api/frontend/v1/stv/dokumente/updateAkte/' + akte_id, + data); + }, + deleteFile(akte_id){ + return this.$fhcApi.post('api/frontend/v1/stv/dokumente/deleteAkte/' + akte_id); + }, + uploadFile(prestudent_id, data){ + return this.$fhcApi.post('api/frontend/v1/stv/dokumente/uploadDokument/' + prestudent_id, + data); + }, +} \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente.js new file mode 100644 index 000000000..01ca2d373 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente.js @@ -0,0 +1,18 @@ +import ViewDocuments from "./Dokumente/Dokumente.js"; + +export default { + name:"TabDocuments", + components: { + ViewDocuments + }, + props: { + modelValue: Object, + }, + data(){ + return {} + }, + template: ` +
+ +
` +}; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Dokumente.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Dokumente.js new file mode 100644 index 000000000..be69ae70d --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Dokumente.js @@ -0,0 +1,51 @@ +import DocumentsUnaccepted from './List/Unaccepted.js'; +import DocumentsAccepted from './List/Accepted.js'; + +export default { + components: { + DocumentsUnaccepted, + DocumentsAccepted + }, + props: { + student: Object + }, + data(){ + return { + listMissingDocuments: [] + } + }, + methods: { + reloadUnaccepted(){ + this.$refs.tableUnaccepted.reload(); + }, + reloadAccepted(){ + this.$refs.tableAccepted.reload(); + }, + }, + template: ` +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ ` +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Accepted.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Accepted.js new file mode 100644 index 000000000..7790e12f8 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Accepted.js @@ -0,0 +1,335 @@ +import {CoreFilterCmpt} from "../../../../../filter/Filter.js"; +import ModalEdit from "../Modal/Edit.js"; +import ModalUpload from "../Modal/Upload.js"; + +import ApiStvDocuments from "../../../../../../api/factory/stv/documents.js"; + +export default { + components: { + name: "AcceptedDocuments", + CoreFilterCmpt, + ModalEdit, + ModalUpload + }, + props: { + prestudent_id: Number, + studiengang_kz: Number + }, + data(){ + return { + tabulatorOptions: { + ajaxURL: 'dummy', + ajaxRequestFunc: () => this.$api.call( + ApiStvDocuments.getDocumentsAccepted({ + id: this.prestudent_id, + studiengang_kz: this.studiengang_kz}) + ), + ajaxResponse: (url, params, response) => { + return response.data; + }, + columns: [ + {title: "Dokument", field: "bezeichnung"}, + {title: "Akzeptiertdatum", field: "docdatum", + formatter: function (cell) { + const dateStr = cell.getValue(); + if (!dateStr) return ""; + + const date = new Date(dateStr); + return date.toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour12: false + }); + }}, + {title: "Akzeptiertvon", field: "insertvonma"}, + { + title: "UploadDatum", field: "hochgeladenamum", + formatter: function (cell) { + const dateStr = cell.getValue(); + if (!dateStr) return ""; + + const date = new Date(dateStr); + return date.toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour12: false + }); + }}, + {title: "Kurzbz", field: "dokument_kurzbz", visible: false}, + {title: "Prestudent ID", field: "prestudent_id", visible: false}, + {title: "nachgereicht", field: "nachgereicht", visible: false, + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + }}, + {title: "infotext", field: "infotext"}, + {title: "akte_id", field: "akte_id"}, + {title: "dms_id", field: "dms_id", visible: false}, + {title: "titel", field: "titel_intern"}, + {title: "vorhanden", field: "vorhanden", + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + }}, + {title: "Anmerkung_intern", field: "anmerkung_intern", visible: false}, + {title: "Nachreichung am", field: "nachgereicht_am", + formatter: function (cell) { + const dateStr = cell.getValue(); + if (!dateStr) return ""; + + const date = new Date(dateStr); + return date.toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour12: false + }); + }}, + { + title: 'Aktionen', field: 'actions', + minWidth: 50, + maxWidth: 100, + formatter: (cell, formatterParams, onRendered) => { + + let container = document.createElement('div'); + container.className = "d-flex gap-2"; + + if(cell.getData().vorhanden){ + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'downloadDok'); + button.addEventListener('click', (event) => + this.actionDownloadFile(cell.getData().akte_id) + ); + container.append(button); + + button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'editDokument'); + button.addEventListener('click', (event) => + this.actionEditDocument(cell.getData().akte_id) + ); + container.append(button); + + button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'deleteDokument'); + button.addEventListener('click', () => + this.actionDeleteFile(cell.getData().akte_id) + ); + container.append(button); + } + else + { + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'uploadDokument'); + button.addEventListener('click', () => + this.actionUploadFile(cell.getData().dokument_kurzbz) + ); + container.append(button); + } + + + return container; + }, + frozen: true + }, + ], + layout: 'fitColumns', + layoutColumnsOnNewData: false, + height: 300, + selectable: true, + selectableRangeMode: 'click', + persistenceID: 'core-details-documents-accepted', + listDocuments: [], + }, + tabulatorEvents: [ + { + event: 'tableBuilt', + handler: async () => { + await this.$p.loadCategory(['global', 'dokumente', 'ui', 'mobility', 'ampeln']); + + let cm = this.$refs.table.tabulator.columnManager; + + cm.getColumnByField('bezeichnung').component.updateDefinition({ + title: this.$p.t('global', 'dokument') + }); + cm.getColumnByField('docdatum').component.updateDefinition({ + title: this.$p.t('dokumente', 'datumAkzeptiert') + }); + cm.getColumnByField('dokument_kurzbz').component.updateDefinition({ + title: this.$p.t('mobility', 'kurzbz') + }); + cm.getColumnByField('insertvonma').component.updateDefinition({ + title: this.$p.t('global', 'uploaddatum') + }); + cm.getColumnByField('hochgeladenamum').component.updateDefinition({ + title: this.$p.t('global', 'uploaddatum') + }); + cm.getColumnByField('nachgereicht').component.updateDefinition({ + title: this.$p.t('dokumente', 'nachgereicht') + }); + cm.getColumnByField('vorhanden').component.updateDefinition({ + title: this.$p.t('dokumente', 'vorhanden') + }); + cm.getColumnByField('dms_id').component.updateDefinition({ + title: this.$p.t('global', 'dms_id') + }); + cm.getColumnByField('titel_intern').component.updateDefinition({ + title: this.$p.t('global', 'titel') + }); + cm.getColumnByField('anmerkung_intern').component.updateDefinition({ + title: this.$p.t('global', 'anmerkung') + }); + cm.getColumnByField('akte_id').component.updateDefinition({ + title: this.$p.t('global', 'akte_id') + }); + cm.getColumnByField('nachgereicht_am').component.updateDefinition({ + title: this.$p.t('global', 'dokument') + }); + } + } + ] + } + }, + computed: { + tabulatorEvents() { + const events = [ + { + event: "rowDblClick", + handler: (e, row) => { + this.actionDownloadFile(row.getData().akte_id); + } + } + ]; + return events; + } + }, + methods: { + actionDownloadFile(akte_id){ + window.open( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/api/frontend/v1/stv/dokumente/download?akte_id=' + encodeURIComponent(akte_id), + '_blank' + ); + }, + actionUploadFile(dokument_kurzbz){ + this.$refs.modalUpload.open(this.prestudent_id, dokument_kurzbz); + }, + actionEditDocument(akte_id){ + this.$refs.modalEdit.open(akte_id); + }, + actionDeleteFile(akte_id){ + this.$fhcAlert + .confirmDelete() + .then(result => result + ? akte_id + : Promise.reject({handled: true})) + .then(this.deleteFile) + .catch(this.$fhcAlert.handleSystemError); + }, + deleteFile(akte_id){ + return this.$api + .call(ApiStvDocuments.deleteFile(akte_id)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(()=> { + this.reload(); + }); + }, + deleteZuordnung(selected) { + Promise.allSettled( + selected.map(e => + this.$api + .call(ApiStvDocuments.deleteZuordnung({ + prestudent_id: this.prestudent_id, + dokument_kurzbz: e.dokument_kurzbz + })) + .then(() => ({ + success: true, + dokument_bz: e.bezeichnung + })) + .catch(() => ({ + success: false, + dokument_bz: e.bezeichnung + })) + ) + ).then(results => { + const failed = results.filter(res => !res.value.success); + const suceeded = results.filter(res => res.value.success); + if (failed.length > 0) { + failed.forEach(res => { + this.$fhcAlert.alertError(this.$p.t('dokumente', 'errorUnaccepted', + {'dokument_kurzbz': res.value.dokument_bz} + )); + }); + let countSuceeded = suceeded.length; + if(countSuceeded > 0) + this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successCountUnaccepted', + {'count': countSuceeded})); + + } else { + this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successUnaccepted')); + } + this.reloadAll(); + }); + }, + + reload(){ + this.$refs.table.reloadTable(); + }, + reloadAll(){ + this.reload(); + this.$emit('reloadUnaccepted'); + } + }, + template: ` +
+
{{$p.t('dokumente', 'accepted')}}
+ + + + + + + + + + +
+ ` +} \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js new file mode 100644 index 000000000..6cc25cb6f --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js @@ -0,0 +1,347 @@ +import {CoreFilterCmpt} from "../../../../../filter/Filter.js"; +import ModalEdit from "../Modal/Edit.js"; +import ModalUpload from "../Modal/Upload.js"; + +import ApiStvDocuments from "../../../../../../api/factory/stv/documents.js"; + +export default { + name: "UnacceptedDocuments", + components: { + CoreFilterCmpt, + ModalEdit, + ModalUpload + }, + props: { + prestudent_id: Number, + studiengang_kz: Number + }, + data(){ + return { + tabulatorOptions: { + ajaxURL: 'dummy', + ajaxRequestFunc: () => this.$api.call( + ApiStvDocuments.getDocumentsUnaccepted({ + id: this.prestudent_id, + studiengang_kz: this.studiengang_kz}) + ), + ajaxResponse: (url, params, response) => response.data, + columns: [ + {title: "Dokument", field: "bezeichnung"}, + {title: "Kurzbz", field: "dokument_kurzbz", visible: false}, + { + title: "UploadDatum", field: "hochgeladenamum", + formatter: function (cell) { + const dateStr = cell.getValue(); + if (!dateStr) return ""; + + const date = new Date(dateStr); + return date.toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour12: false + }); + } + }, + {title: "nachgereicht", field: "nachgereicht", visible: false, + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + }}, + {title: "vorhanden", + field: "vorhanden", + formatter: function(cell, formatterParams, onRendered) { + let value = cell.getValue(); + let rowData = cell.getRow().getData(); // Zugriff auf gesamte Zeile + let dueDate = rowData["nachgereicht_am"]; + const date = new Date(dueDate); + let dateFormatted = date.toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour12: false + }); + + let tickCrossIcon = value + ? '' + : ''; + + // if nachgereicht_am show datum + let pill = dueDate + ? `${dateFormatted}` + : ''; + + return `${tickCrossIcon} ${pill}`; + }, + hozAlign: "center" + }, + {title: "infotext", field: "infotext"}, + {title: "akte_id", field: "akte_id"}, + {title: "titel_intern", field: "titel_intern"}, + {title: "Anmerkung_intern", field: "anmerkung_intern", visible: false}, + {title: "Online", field: "onlinebewerbung", + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + }}, + {title: "Pflicht", field: "pflicht", + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + }}, + {title: "Nachreichung am", field: "nachgereicht_am", + formatter: function (cell) { + const dateStr = cell.getValue(); + if (!dateStr) return ""; + + const date = new Date(dateStr); + return date.toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour12: false + }); + }}, + { + title: 'Aktionen', field: 'actions', + minWidth: 50, + maxWidth: 100, + formatter: (cell, formatterParams, onRendered) => { + + let container = document.createElement('div'); + container.className = "d-flex gap-2"; + + if(cell.getData().akte_id){ + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'downloadDok'); + button.addEventListener('click', (event) => + this.actionDownloadFile(cell.getData().akte_id) + ); + container.append(button); + + button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'editDokument'); + button.addEventListener('click', (event) => + this.actionEditFile(cell.getData().akte_id) + ); + container.append(button); + + button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'deleteDokument'); + button.addEventListener('click', () => + this.actionDeleteFile(cell.getData().akte_id) + ); + container.append(button); + } + else + { + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'uploadDokument'); + button.addEventListener('click', () => + this.actionUploadFile(cell.getData().dokument_kurzbz) + ); + container.append(button); + } + + return container; + }, + frozen: true + }, + ], + layout: 'fitColumns', + layoutColumnsOnNewData: false, + height: 300, + selectable: true, + selectableRangeMode: 'click', + persistenceID: 'core-details-documents-unaccepted', + listDocuments: [], + prestudentDocumentData: [], + }, + tabulatorEvents: [ + { + event: 'tableBuilt', + handler: async () => { + await this.$p.loadCategory(['global', 'dokumente', 'ui', 'mobility', 'ampeln']); + + let cm = this.$refs.table.tabulator.columnManager; + + cm.getColumnByField('bezeichnung').component.updateDefinition({ + title: this.$p.t('global', 'dokument') + }); + cm.getColumnByField('dokument_kurzbz').component.updateDefinition({ + title: this.$p.t('mobility', 'kurzbz') + }); + cm.getColumnByField('hochgeladenamum').component.updateDefinition({ + title: this.$p.t('global', 'uploaddatum') + }); + cm.getColumnByField('nachgereicht').component.updateDefinition({ + title: this.$p.t('dokumente', 'nachgereicht') + }); + cm.getColumnByField('vorhanden').component.updateDefinition({ + title: this.$p.t('dokumente', 'vorhanden') + }); + cm.getColumnByField('anmerkung_intern').component.updateDefinition({ + title: this.$p.t('global', 'anmerkung') + }); + cm.getColumnByField('akte_id').component.updateDefinition({ + title: this.$p.t('global', 'akte_id') + }); + cm.getColumnByField('pflicht').component.updateDefinition({ + title: this.$p.t('ampeln', 'mandatory') + }); + cm.getColumnByField('nachgereicht_am').component.updateDefinition({ + title: this.$p.t('global', 'dokument') + }); + } + } + ] + } + }, + computed: { + tabulatorEvents() { + const events = [ + { + event: "rowDblClick", + handler: (e, row) => { + this.actionDownloadFile(row.getData().akte_id); + } + } + ]; + + return events; + } + }, + methods: { + actionDownloadFile(akte_id){ + window.open( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/api/frontend/v1/stv/dokumente/download?akte_id=' + encodeURIComponent(akte_id), + '_blank' + ); + }, + actionUploadFile(dokument_kurzbz){ + this.$refs.modalUpload.open(this.prestudent_id, dokument_kurzbz); + }, + actionDeleteFile(akte_id){ + this.$fhcAlert + .confirmDelete() + .then(result => result + ? akte_id + : Promise.reject({handled: true})) + .then(this.deleteFile) + .catch(this.$fhcAlert.handleSystemError); + }, + actionEditFile(akte_id){ + this.$refs.modalEdit.open(akte_id); + }, + acceptDocuments(selected){ + Promise + .allSettled( + selected.map(e => + this.$api + .call(ApiStvDocuments.createZuordnung({ + prestudent_id: this.prestudent_id, + dokument_kurzbz: e.dokument_kurzbz + })) + .then(() => ({ + success: true, + dokument_bz: e.bezeichnung + })) + .catch(() => ({ + success: false, + dokument_bz: e.bezeichnung + })) + ) + ) + .then(results => { + const failed = results.filter(res => !res.value.success); + const suceeded = results.filter(res => res.value.success); + if (failed.length > 0) { + failed.forEach(res => { + this.$fhcAlert.alertError(this.$p.t('dokumente', 'errorAccepted', + {'dokument_kurzbz': res.value.dokument_bz} + )); + }); + let countSuceeded = suceeded.length; + if(countSuceeded > 0) + this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successCountAccepted', + {'count': countSuceeded})); + + } else { + this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successAccepted')); + } + this.reloadAll(); + }); + }, + deleteFile(akte_id){ + return this.$fhcApi.factory.stv.documents.deleteFile(akte_id) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(()=> { + this.reload(); + }); + }, + reload(){ + this.$refs.table.reloadTable(); + }, + reloadAll() { + this.reload(); + this.$emit('reloadAccepted'); + } + }, + template: ` +
+
{{$p.t('dokumente', 'unaccepted')}}
+ + + + + + + + + + +
+ ` +} \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Modal/Edit.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Modal/Edit.js new file mode 100644 index 000000000..29c98020e --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Modal/Edit.js @@ -0,0 +1,141 @@ +import BsModal from "../../../../../Bootstrap/Modal.js"; +import FormForm from "../../../../../Form/Form.js"; +import FormInput from "../../../../../Form/Input.js"; + +import ApiStvDocuments from "../../../../../../api/factory/stv/documents.js"; + +export default { + name: "modalEditDocuments", + components: { + BsModal, + FormForm, + FormInput + }, + data(){ + return { + formData: [], + listDokTypen: [] + } + }, + methods: { + updateFile(akte_id){ + this.$api + .call(ApiStvDocuments.updateFile(akte_id, this.formData)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + this.$refs.modalEditDocument.hide(); + this.$emit('reload'); + }) + .catch(this.$fhcAlert.handleSystemError); + }, + open(akte_id){ + this.loadFormData(akte_id); + this.$refs.modalEditDocument.show(); + }, + loadFormData(akte_id){ + return this.$api + .call(ApiStvDocuments.loadAkte(akte_id)) + .then(result => { + this.formData = result.data; + return result; + }) + .catch(this.$fhcAlert.handleSystemError); + } + }, + created(){ + this.$api + .call(ApiStvDocuments.getDoktypen()) + .then(result => { + this.listDokTypen = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + }, + template: ` + + + + +
+
+ + + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+ `, +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Modal/Upload.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Modal/Upload.js new file mode 100644 index 000000000..f6280a9cf --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/Modal/Upload.js @@ -0,0 +1,127 @@ +import BsModal from "../../../../../Bootstrap/Modal.js"; +import FormForm from "../../../../../Form/Form.js"; +import FormInput from "../../../../../Form/Input.js"; +import FormUploadDms from "../../../../../Form/Upload/Dms.js"; + +import ApiStvDocuments from "../../../../../../api/factory/stv/documents.js"; + +export default { + name: "modalUploadDocuments", + components: { + BsModal, + FormForm, + FormInput, + FormUploadDms + }, + data(){ + return{ + formData: { + dokument_kurzbz: null, + anhang: [], + anmerkung_intern: null + }, + listDokTypen: [] + } + }, + methods:{ + open(prestudent_id, dokument_kurzbz){ + this.formData.dokument_kurzbz = dokument_kurzbz; + this.formData.prestudent_id = prestudent_id; + this.$refs.modalUploadFile.show(); + }, + uploadFile(){ + // console.log(this.formData.anhang[0]); + return this.$api + .call(ApiStvDocuments.uploadFile(this.formData.prestudent_id, this.formData)) + .then(result => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successUpload')); + this.resetModal(); + this.$refs.modalUploadFile.hide(); + this.$emit('reload'); + }) + .catch(this.$fhcAlert.handleSystemError); + }, + resetModal(){ + this.formData.dokument_kurzbz = null; + this.formData.anhang = []; + this.formData.anmerkung_intern = null; + this.formData.titel_intern = null; + } + }, + created(){ + this.$api + .call(ApiStvDocuments.getDoktypen()) + .then(result => { + this.listDokTypen = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + }, + template: ` + + + + + +
+
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+ `, +} \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 53df09ca9..7ce22f0a0 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -42123,6 +42123,688 @@ and represent the current state of research on the topic. The prescribed citatio ), /////////// FHC4 Phrases Messages END /////////// + // FHC4 Studierendenverwaltung Dokumente START + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'tab_documents', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokumente', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Documents', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'uploadDokument', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokument hochladen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Upload document', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'deleteDokument', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokument löschen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Delete document', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'editDokument', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokument bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Edit document', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'title', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Titel', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'title', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'dokTyp', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokumententyp', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Document type', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'nachreichungAm', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nachreichung am', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Submission on', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'dokDetails', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokumentendetails', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Document Details', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'anmerkung_person', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anmerkung der Person', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Notes of the Person', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'successUpload', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokument erfolgreich hochgeladen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Dokumentupload successful', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'dokumentAccept', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akzeptieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Accept', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'dokumentUnaccept', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Entakzeptieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Unaccept', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'accepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akzeptiert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Accepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'unaccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nicht Akzeptiert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Not Accepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'err_deleteDokHere', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dieses Dokument darf hier nicht gelöscht werden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'This document may not be deleted here', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'err_updateNotAllowed', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es können nur Eintraege geändert werden zu denen Dokumente hochgeladen wurden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Only entries to which documents have been uploaded can be changed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'downloadDok', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokument herunterladen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Download Document', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'successAccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akzeptieren erfolgreich', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Successfully set to accepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'successUnaccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Entakzeptieren erfolgreich', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Successfully set to unaccepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'successCountUnaccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Entakzeptieren von {count} Dokument(en) erfolgreich', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '{count} Document(s) successfully set to unaccepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'successCountAccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akzeptieren von {count} Dokument(en) erfolgreich', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '{count} Document(s) successfully set to accepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'errorUnaccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Fehler beim Entakzeptieren von Dokument {dokument_kurzbz}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error unaccepting document {dokument_kurzbz}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'errorAccepted', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Fehler beim Akzeptieren von Dokument {dokument_kurzbz}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error Accepting document {dokument_kurzbz}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'errorMissingValue', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '{value} fehlt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '{value} missing', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_valueNotNumeric', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '{value} darf nur Ziffern enthalten.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '{value} must contain only numbers.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'nachgereicht', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nachgereicht', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Submitted later', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'vorhanden', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Vorhanden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Available', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'akte_id', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akte ID', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'File ID', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'nachgereicht_am', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nachgereicht am', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Submitted on', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'datumAkzeptiert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akzeptiertdatum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Accepted on', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'akzeptiertVon', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Akzeptiert von', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Accepted by', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'global', + 'phrase' => 'dms_id', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dms ID', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Dms ID', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'error_fileMissing', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte eine Datei anhängen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please attach a file', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dokumente', + 'phrase' => 'error_fileType', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nur Dateitypen JPEG, PNG oder PDF sind erlaubt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Only JPEG, PNG, or PDF files are allowed.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // FHC4 Studierendenverwaltung Dokumente END );