diff --git a/application/config/stv.php b/application/config/stv.php new file mode 100644 index 000000000..7f2d1aa30 --- /dev/null +++ b/application/config/stv.php @@ -0,0 +1,56 @@ + [ + //all fields can be configured to be hidden, see class attribute stv-details-details-name for name + 'hiddenFields' => [], + 'hideUDFs' => false + ], + + 'prestudent' => [ + + //all fields can be configured to be hidden, see class attribute stv-details-prestudent-name for name + 'hiddenFields' => [ + + //propably used by FH-Communities + 'aufnahmeschluessel', 'standort_code', 'facheinschlaegigBerufstaetig' + + ], + 'hideUDFs' => false + ], + 'finalexam' => [ + 'documents' => [ + 'pruefungsprotokoll' => [ + 'de' => [ + 'Bakk' => 'PrProtBA', + 'Master' => 'PrProtMA', + ], + 'en' => [ + 'Bakk' => 'PrProtBAEng', + 'Master' => 'PrProtMAEng', + ], + ], + 'pruefungszeugnis' => [ + 'de' => [ + 'Bakk' => 'Bakkzeugnis', + 'Master' => 'Diplomzeugnis', + ], + 'en' => [ + 'Bakk' => 'BakkzeugnisEng', + 'Master' => 'DiplomzeugnisEng', + ], + ], + 'urkunde' => [ + 'de' => [ + 'Bakk' => 'Bakkurkunde', + 'Master' => 'Diplomurkunde', + ], + 'en' => [ + 'Bakk' => 'BakkurkundeEng', + 'Master' => 'DiplomurkundeEng', + ], + ], + ], + ] + ]; \ No newline at end of file diff --git a/application/controllers/Studentenverwaltung.php b/application/controllers/Studentenverwaltung.php index e09d04c6a..2ce19c58a 100644 --- a/application/controllers/Studentenverwaltung.php +++ b/application/controllers/Studentenverwaltung.php @@ -30,7 +30,8 @@ class Studentenverwaltung extends Auth_Controller 'admin' => $this->permissionlib->isBerechtigt('admin'), 'assistenz_schreibrechte' => $this->permissionlib->isBerechtigt('assistenz','suid'), 'student/keine_studstatuspruefung' => $this->permissionlib->isBerechtigt('student/keine_studstatuspruefung'), - 'lehre/reihungstestAufsicht' => $this->permissionlib->isBerechtigt('lehre/reihungstestAufsicht') + 'lehre/reihungstestAufsicht' => $this->permissionlib->isBerechtigt('lehre/reihungstestAufsicht'), + 'system/change_outputformat' => $this->permissionlib->getOE_isEntitledFor('system/change_outputformat'), ], 'variables' => [ 'semester_aktuell' => $this->variablelib->getVar('semester_aktuell') diff --git a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php index c749b1160..c9bc3ab3f 100644 --- a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php +++ b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php @@ -22,7 +22,7 @@ class Abschlusspruefung extends FHCAPI_Controller 'getMitarbeiter' => ['admin:rw', 'assistenz:rw'], 'getPruefer' => ['admin:rw', 'assistenz:rw'], 'getTypStudiengang' => ['admin:rw', 'assistenz:rw'], - + 'checkForExistingExams' => ['admin:rw', 'assistenz:rw'], ]); // Load Libraries @@ -149,6 +149,17 @@ class Abschlusspruefung extends FHCAPI_Controller { $studiengang_kz= $this->input->post('studiengang_kz'); +/* if (!$studiengang_kzs || !is_array($studiengang_kzs)) { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('studiengang_kzs', '', 'required|is_null', [ + 'is_null' => $this->p->t('ui', 'error_fieldMustBeArray') + ]); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + }*/ + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); @@ -211,11 +222,8 @@ class Abschlusspruefung extends FHCAPI_Controller return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL); } - $formData = $this->input->post('formData'); - - $_POST['pruefungstyp_kurzbz'] = $formData['pruefungstyp_kurzbz']; $_POST['akadgrad_id']= $formData['akadgrad_id']; $_POST['vorsitz'] = isset($formData['vorsitz']['mitarbeiter_uid']) ? $formData['vorsitz']['mitarbeiter_uid'] : $formData['vorsitz']; @@ -254,7 +262,7 @@ class Abschlusspruefung extends FHCAPI_Controller 'vorsitz' => $this->input->post('vorsitz'), 'pruefungsantritt_kurzbz' => $this->input->post('pruefungsantritt_kurzbz'), 'abschlussbeurteilung_kurzbz' => $this->input->post('abschlussbeurteilung_kurzbz'), - 'datum' => $this->input->post('datum'), + 'datum' => $this->input->post('datum'), //TODO(Manu) check if minute format like FAS 'sponsion' => $this->input->post('sponsion'), 'pruefer1' => $this->input->post('pruefer1'), 'pruefer2' => $this->input->post('pruefer2'), @@ -363,4 +371,37 @@ class Abschlusspruefung extends FHCAPI_Controller } return $this->terminateWithSuccess(current(getData($result)) ? : null); } + + public function checkForExistingExams() + { + $warning = false; + $output = []; + + $student_uids = $this->input->post('uids'); + + if (empty($student_uids)) { + throw new InvalidArgumentException("Keine UID(s) übergeben."); + } + + foreach ($student_uids as $uid) + { + $result = $this->AbschlusspruefungModel->loadWhere( + array('student_uid' => $uid) + ); + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + if (!hasData($result)) + { + $warning = true; + $output[] = $uid; + } + } + if($warning) + { + $uids = is_array($output) ? implode(", ", $output) : $output; + return $this->terminateWithError($this->p->t('abschlusspruefung', 'error_studentOhneFinalExam', ['id'=> $uids]), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess('step3'); + } } diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 72058f001..3d8fa5759 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -44,16 +44,24 @@ class Config extends FHCAPI_Controller 'person', 'lehre', 'stv', - 'konto' + 'konto', + 'abschlusspruefung' ]); + + //TODO(Manu) in clean up version bereits vorhanden: delete before merge + // Load Config + $this->load->config('stv'); } public function student() { $result = []; + $config = $this->config->item('tabs'); + $result['details'] = [ 'title' => $this->p->t('stv', 'tab_details'), - 'component' => './Stv/Studentenverwaltung/Details/Details.js' + 'component' => './Stv/Studentenverwaltung/Details/Details.js', + 'config' => $config['details'] ]; $result['notes'] = [ 'title' => $this->p->t('stv', 'tab_notes'), @@ -99,7 +107,8 @@ class Config extends FHCAPI_Controller */ $result['finalexam'] = [ 'title' => $this->p->t('stv', 'tab_finalexam'), - 'component' => './Stv/Studentenverwaltung/Details/Abschlusspruefung.js' + 'component' => './Stv/Studentenverwaltung/Details/Abschlusspruefung.js', + 'config' => $config['finalexam'] ]; Events::trigger('stv_conf_student', function & () use (&$result) { @@ -112,6 +121,7 @@ class Config extends FHCAPI_Controller public function students() { $result = []; + $config = $this->config->item('tabs'); $result['banking'] = [ 'title' => $this->p->t('stv', 'tab_banking'), 'component' => './Stv/Studentenverwaltung/Details/Konto.js', @@ -135,6 +145,11 @@ class Config extends FHCAPI_Controller 'changeStatusToAbsolvent' => $this->permissionlib->isBerechtigt('admin') ] ]; + $result['finalexam'] = [ + 'title' => $this->p->t('stv', 'tab_finalexam'), + 'component' => './Stv/Studentenverwaltung/Details/Abschlusspruefung.js', + 'config' => $config['finalexam'] + ]; Events::trigger('stv_conf_students', function & () use (&$result) { return $result; diff --git a/public/css/Studentenverwaltung.css b/public/css/Studentenverwaltung.css index f179c3667..7cf900e5d 100644 --- a/public/css/Studentenverwaltung.css +++ b/public/css/Studentenverwaltung.css @@ -52,6 +52,31 @@ html { height: 0; } +/* Dropdown AbschlusspruefungList.js */ +.tabulator-row { + overflow: visible !important; +} + +.tabulator-cell { + overflow: visible !important; +} + +.p-dropdown-item{ + z-index: 1201 !important; +} + +.l-dropdown-item{ + z-index: 1301 !important; +} + +.tabulator-row .dropdown-menu { + position: absolute; + z-index: 1204; /* Ensure it's above other elements */ +} +.tabulator-table { + overflow: visible !important; +} + @media (min-width: 768px) { #sidebarMenu { visibility: visible!important; diff --git a/public/js/api/stv/abschlusspruefung.js b/public/js/api/stv/abschlusspruefung.js index 56ce6b4fd..d51c760bc 100644 --- a/public/js/api/stv/abschlusspruefung.js +++ b/public/js/api/stv/abschlusspruefung.js @@ -41,4 +41,8 @@ export default { getNoten(){ return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getNoten/'); }, + checkForExistingExams(uids) { + return this.$fhcApi.post('api/frontend/v1/stv/abschlusspruefung/checkForExistingExams/', {uids} + ); + } } \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index 7f663c159..e07f7e592 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -56,6 +56,7 @@ export default { hasAdminPermission: this.permissions['admin'], hasPermissionToSkipStatusCheck: this.permissions['student/keine_studstatuspruefung'], hasPermissionRtAufsicht: this.permissions['lehre/reihungstestAufsicht'], + hasPermissionOutputformat: this.permissions['system/change_outputformat'], lists: this.lists, defaultSemester: this.defaultSemester, $reloadList: () => { diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung.js b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung.js index ef6635660..a40b573a4 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung.js @@ -1,11 +1,17 @@ -import AbschlusspruefungList from "./Abschlusspruefung/AbschlusspruefungList.js"; +import AbschlussPruefung from "./Abschlusspruefung/Abschlusspruefung.js"; export default { components: { - AbschlusspruefungList + AbschlussPruefung + }, + provide() { + return { + config: this.config + }; }, props: { - modelValue: Object + modelValue: Object, + config: Object }, data(){ return {} @@ -13,6 +19,6 @@ export default { template: `
- +
` }; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/AbschlusspruefungList.js b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js similarity index 63% rename from public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/AbschlusspruefungList.js rename to public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js index aa59a8cca..892becd67 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/AbschlusspruefungList.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Abschlusspruefung/Abschlusspruefung.js @@ -2,8 +2,9 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; import BsModal from "../../../../Bootstrap/Modal.js"; import FormForm from '../../../../Form/Form.js'; import FormInput from '../../../../Form/Input.js'; -import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"; - +import PvAutoComplete + from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"; +import AbschlusspruefungDropdown from "./AbschlusspruefungDropdown"; export default { components: { @@ -11,12 +12,44 @@ export default { BsModal, FormForm, FormInput, - PvAutoComplete + PvAutoComplete, + AbschlusspruefungDropdown }, inject: { cisRoot: { from: 'cisRoot' }, + config: { + from: 'config', + required: true + }, + $reloadList: { + from: '$reloadList', + required: true + }, + isBerechtigtDocAndOdt: { + from: 'hasPermissionOutputformat', + default: false + } + }, + computed: { + studentUids() { + if (this.student.uid) + { + return [this.student.uid]; + } + return this.student.map(e => e.uid); + }, + studentKzs(){ + if (this.student.uid) + { + return [this.student.studiengang_kz]; + } + return this.student.map(e => e.studiengang_kz); + }, + stg_kz(){ + return this.studentKzs[0]; + } }, props: { student: Object @@ -66,22 +99,13 @@ export default { button = document.createElement('button'); button.className = 'btn btn-outline-secondary btn-action'; button.innerHTML = ''; - button.title = this.$p.t('ui', 'delete'); + button.title = this.$p.t('ui', 'loeschen'); button.addEventListener('click', () => this.actionDeleteAbschlusspruefung(cell.getData().abschlusspruefung_id) ); container.append(button); - //TODO(Manu) umbau auf dropdown? - //Prüfungsprotokoll, -zeugnis, -urkunde jeweils Deutsch und English - button = document.createElement('button'); - button.className = 'btn btn-outline-secondary btn-action'; - button.innerHTML = ''; - button.title = "Drucken"; - button.addEventListener('click', () => - this.actionPrintAbschlusspruefung(cell.getData().abschlusspruefung_id) - ); - container.append(button); + container.append(cell.getData().actionDiv); return container; }, @@ -91,48 +115,75 @@ export default { layout: 'fitDataFill', layoutColumnsOnNewData: false, height: 'auto', + minHeight: '200', selectable: true, index: 'abschlusspruefung_id', persistenceID: 'stv-details-finalexam' }, tabulatorEvents: [ { - /* event: 'tableBuilt', - handler: async() => { - await this.$p.loadCategory(['global','person']); + event: 'dataLoaded', + handler: data => this.tabulatorData = data.map(item => { + item.actionDiv = document.createElement('div'); + return item; + }), + }, + { + event: 'tableBuilt', + handler: async() => { + await this.$p.loadCategory(['global', 'person', 'stv', 'abschlusspruefung', 'ui']); - let cm = this.$refs.table.tabulator.columnManager; - cm.getColumnByField('name').component.updateDefinition({ - title: this.$p.t('global', 'name') - }); + let cm = this.$refs.table.tabulator.columnManager; - cm.getColumnByField('typ').component.updateDefinition({ - title: this.$p.t('global', 'typ') - }); - cm.getColumnByField('anschrift').component.updateDefinition({ - title: this.$p.t('person', 'anschrift') - }); - cm.getColumnByField('kontonr').component.updateDefinition({ - title: this.$p.t('person', 'kontonr') - }); - cm.getColumnByField('blz').component.updateDefinition({ - title: this.$p.t('person', 'blz') - }); - cm.getColumnByField('verrechnung').component.updateDefinition({ - title: this.$p.t('person', 'verrechnung') - }); - cm.getColumnByField('person_id').component.updateDefinition({ - title: this.$p.t('person', 'person_id') - }); - cm.getColumnByField('abschlusspruefung_id').component.updateDefinition({ - title: this.$p.t('ui', 'abschlusspruefung_id') - });*/ - /* cm.getColumnByField('actions').component.updateDefinition({ - title: this.$p.t('global', 'aktionen') - });*/ + cm.getColumnByField('vorsitz_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'vorsitz_header') + }); + cm.getColumnByField('beurteilung_bezeichnung').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'abschlussbeurteilung') + }); + cm.getColumnByField('p1_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefer1') + }); + cm.getColumnByField('p2_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefer2') + }); + cm.getColumnByField('p3_nachname').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefer3') + }); + cm.getColumnByField('format_datum').component.updateDefinition({ + title: this.$p.t('global', 'datum') + }); + cm.getColumnByField('uhrzeit').component.updateDefinition({ + title: this.$p.t('global', 'uhrzeit') + }); + cm.getColumnByField('format_freigabedatum').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'freigabe') + }); + cm.getColumnByField('antritt_bezeichnung').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'pruefungsantritt') + }); + cm.getColumnByField('format_sponsion').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'sponsion') + }); + cm.getColumnByField('anmerkung').component.updateDefinition({ + title: this.$p.t('global', 'anmerkung') + }); + cm.getColumnByField('pruefungstyp_kurzbz').component.updateDefinition({ + title: this.$p.t('global', 'typ') + }); + cm.getColumnByField('abschlusspruefung_id').component.updateDefinition({ + title: this.$p.t('ui', 'abschlusspruefung_id') + }); + /* + cm.getColumnByField('actions').component.updateDefinition({ + title: this.$p.t('global', 'aktionen') + }); + */ + } } ], + tabulatorData: [], lastSelected: null, formData: { typStg: null, @@ -163,9 +214,25 @@ export default { mitarbeiter: null, pruefer: null }, + stgTyp: null, + stgKz: null, + } + }, + watch: { + student(){ + if (this.$refs.table) { + this.$refs.table.reloadTable(); + } + this.getStudiengangsTyp(); } }, methods: { + getStudiengangsTyp(){ + this.stgTyp = ''; + this.$fhcApi.factory.stv.abschlusspruefung.getTypStudiengang(this.stg_kz) + .then(result => this.stgTyp = result.data) + .catch(this.$fhcAlert.handleSystemError); + }, actionNewAbschlusspruefung() { this.resetForm(); this.statusNew = true; @@ -185,7 +252,7 @@ export default { .then(this.deleteAbschlusspruefung) .catch(this.$fhcAlert.handleSystemError); }, - addNewAbschlusspruefung(){ + addNewAbschlusspruefung() { const dataToSend = { uid: this.student.uid, formData: this.formData @@ -208,7 +275,8 @@ export default { return this.$fhcApi.factory.stv.abschlusspruefung.loadAbschlusspruefung(abschlusspruefung_id) .then(result => { this.formData = result.data; - this.formData.link = this.cisRoot + 'index.ci.php/lehre/Pruefungsprotokoll/showProtokoll?abschlusspruefung_id='+this.formData.abschlusspruefung_id+'&fhc_controller_id=67481e5ed5490'; + //TODO(Manu) check if cisRoot is okay + this.formData.link = this.cisRoot + 'index.ci.php/lehre/Pruefungsprotokoll/showProtokoll?abschlusspruefung_id=' + this.formData.abschlusspruefung_id + '&fhc_controller_id=67481e5ed5490'; return result; }) .catch(this.$fhcAlert.handleSystemError); @@ -234,28 +302,28 @@ export default { this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); }) .catch(this.$fhcAlert.handleSystemError) - .finally(()=> { + .finally(() => { this.reload(); }); }, resetForm() { - this.formData.pruefungstyp_kurzbz = null; - this.formData.akadgrad_id = null; - this.formData.vorsitz = null; - this.formData.pruefungsantritt_kurzbz = null; - this.formData.abschlussbeurteilung_kurzbz = null; - this.formData.datum = null; //oder new Date(); - this.formData.sponsion = null; - this.formData.pruefer1 = null; - this.formData.pruefer2 = null; - this.formData.pruefer3 = null; - this.formData.anmerkung = null; - this.formData.protokoll = null; - this.formData.note = null; - this.formData.p1 = null; - this.formData.p2 = null; - this.formData.p3 = null; - this.formData.pv= null; + this.formData.pruefungstyp_kurzbz = null; + this.formData.akadgrad_id = null; + this.formData.vorsitz = null; + this.formData.pruefungsantritt_kurzbz = null; + this.formData.abschlussbeurteilung_kurzbz = null; + this.formData.datum = null; //oder new Date(); + this.formData.sponsion = null; + this.formData.pruefer1 = null; + this.formData.pruefer2 = null; + this.formData.pruefer3 = null; + this.formData.anmerkung = null; + this.formData.protokoll = null; + this.formData.note = null; + this.formData.p1 = null; + this.formData.p2 = null; + this.formData.p3 = null; + this.formData.pv = null; }, search(event) { if (this.abortController.mitarbeiter) { @@ -279,31 +347,32 @@ export default { this.filteredPruefer = result.data.retval; }); }, - setDefaultFormData(){ + setDefaultFormData() { this.resetForm(); - - //TODO(Manu) check why phrasen abschlusspruefung not working + //TODO(Manu) test with uid in browser //check lg: if no prüfungsnotizen - if (this.formData.typStg === 'b') - { + if (this.stgTyp === 'b') { + // if (this.stgTyp === 'b' || this.formData.pruefungstyp_kurzbz == 'Bachelor') { this.formData.pruefungstyp_kurzbz = 'Bachelor'; - this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenBachelor'); - this.formData.protokoll = this.$p.t('ui', 'pruefungsnotizenBachelor2'); + + this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster'); } - if (this.formData.typStg === 'd'){ + if (this.stgTyp === 'd' || this.stgTyp === 'm') { this.formData.pruefungstyp_kurzbz = 'Diplom'; this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster'); } - if (this.formData.typStg === 'lg') - { - this.formData.pruefungstyp_kurzbz = 'lgabschluss'; + if (this.stgTyp === 'lg') { + this.formData.pruefungstyp_kurzbz = 'lgabschluss'; } if (!this.formData.akadgrad_id && this.arrAkadGrad.length > 0) { this.formData.akadgrad_id = this.arrAkadGrad[0].akadgrad_id; } - } + }, + printDocument(link) { + window.open(link, '_blank'); + }, }, created() { this.$fhcApi.factory.stv.abschlusspruefung.getTypenAbschlusspruefung() @@ -331,17 +400,34 @@ export default { this.arrAkadGrad = result.data; }) .catch(this.$fhcAlert.handleSystemError); - this.$fhcApi.factory.stv.abschlusspruefung.getTypStudiengang(this.student.studiengang_kz) - .then(result => { - this.formData.typStg = result.data; - this.setDefaultFormData(); - }) - .catch(this.$fhcAlert.handleSystemError); + if (!this.student.length) { + this.$fhcApi.factory.stv.abschlusspruefung.getTypStudiengang(this.student.studiengang_kz) + .then(result => { + this.stgTyp = result.data; + this.setDefaultFormData(); + }) + .catch(this.$fhcAlert.handleSystemError); + } else + this.getStudiengangsTyp(); }, template: `
-

{{this.$p.t('stv','tab_finalexam')}}

+

{{this.$p.t('stv','tab_finalexam')}}

+ +
+ +
+ + + - {{this.$p.t('global','details')}} -

[neu]

+

[{{$p.t('ui', 'neu')}}]

- @@ -380,41 +467,41 @@ export default { v-model="formData.note" name="note" > - - +
- +
- - + +
- -
+ +
- +