| @@ -6,4 +6,6 @@ | |||
| - info@coaching4dogs.xx (kein Admin-Account) | |||
| - benny@probuddy.de | |||
| - timo@tbuddy.de | |||
| - tretslag@gmail.com | |||
| - tretslag@gmail.com | |||
| - `docker exec -it pb-php /bin/bash` | |||
| - SELECT * FROM profile WHERE JSON_CONTAINS( `teams_js`, '{"team_id":"75"}' ); | |||
| @@ -541,7 +541,8 @@ app.core.Dict = { | |||
| "APPOINTMENT_CATEGORIES_MISSING_FOR_ADMIN" : "Deinem Gruppenprofil wurden keine Kategorien zugewiesen. Bitte benachrichtige den Gruppeninhaber.", | |||
| "NOTE_NO_SERIAL_APPOINTMENT_EDITING_AFTER_PUBLISHING" : "Hinweis: Serientermine können nach ihrer Veröffentlichung nur noch einzelnd bearbeitet werden.", | |||
| "STATS_REPORTING" : 'Reporting', | |||
| 'STATS_PLANNING' : 'Planung' | |||
| 'STATS_PLANNING' : 'Planung', | |||
| "DO_YOU_REALLY_WANT_TO_DELETE_THIS_CONTRACT" : "Möchtest Du diesen Vertrag wirklich löschen?", | |||
| }, | |||
| "en" : { | |||
| } | |||
| @@ -12,6 +12,11 @@ const ContractAppointmentAssignDualList = { | |||
| self.contracts = props.get( 'contracts'); | |||
| self.attendedAppointments = props.get( 'attendedAppointments' ); | |||
| self.childObj = {}; | |||
| for (const prop in profile.child_profile_js) { | |||
| self.childObj[profile.child_profile_js[prop]['id']] = profile.child_profile_js[prop]['name']; | |||
| } | |||
| this.render( | |||
| { | |||
| profile : profile, | |||
| @@ -131,6 +136,10 @@ const ContractAppointmentAssignDualList = { | |||
| } | |||
| aSubject += a.subject + ', ' + moment( a.start_dt ).format( 'DD.MM.YYYY' ); | |||
| if (a.attendee_child_id !== null) { | |||
| aSubject += ' (' + self.childObj[a.attendee_child_id] + ')'; | |||
| } | |||
| if ( self.selectedContractId && self.attendedAppointments[ aai ].contractId == self.selectedContractId ) | |||
| { | |||
| $select.append( | |||
| @@ -30,7 +30,6 @@ const ContractCharging = { | |||
| break; | |||
| } | |||
| } | |||
| return m; | |||
| } | |||
| @@ -21,8 +21,7 @@ const ContractEdit = { | |||
| self = this; | |||
| self.contract = null; | |||
| this.render( { groupId : groupId } ); | |||
| self.contractIsDeletable = false; | |||
| app.rpc.call( | |||
| 'Contract', | |||
| @@ -32,11 +31,16 @@ const ContractEdit = { | |||
| }, | |||
| function( res ) | |||
| { | |||
| if ( res && res.hasOwnProperty( 'data' ) && res.data.hasOwnProperty( 'contractData' ) ) | |||
| if ( res && res.hasOwnProperty( 'data' )) | |||
| { | |||
| self.contract = self.createInstance( 'Contract', res.data.contractData ); | |||
| if ( res.data.hasOwnProperty( 'contractData' ) ) { | |||
| self.contract = self.createInstance( 'Contract', res.data.contractData ); | |||
| } | |||
| if ( res.data.hasOwnProperty( 'contractIsDeletable' ) ) { | |||
| self.contractIsDeletable = res.data.contractIsDeletable; | |||
| } | |||
| } | |||
| this.render( { groupId : groupId, contractIsDeletable: self.contractIsDeletable } ); | |||
| const $compContractForm = self.createComponent( | |||
| 'contract-form', | |||
| $container.find( '[f-id="container-contract-form"]' ).first().get( 0 ), | |||
| @@ -89,10 +93,69 @@ const ContractEdit = { | |||
| } | |||
| }); | |||
| if (self.contractIsDeletable) { | |||
| $container.find( '[data-id="btnDeleteContract"]').click( function() | |||
| { | |||
| let $modalRoot = $container.find( '[data-id="modal-confirm-delete-contract"]' ).first(); | |||
| $modalRoot.find( '[data-id="section-confirm-delete-contract"]' ).first().show(); | |||
| $modalRoot.find( '[data-id="section-loader"]' ).first().hide(); | |||
| $modalRoot.on( 'shown.bs.modal', function() | |||
| { | |||
| $modalRoot.find( '[data-id="btnDelete"]' ).first().off( "click" ); | |||
| $modalRoot.find( '[data-id="btnDelete"]' ).first().click( function() | |||
| { | |||
| $(this).off( "click" ); | |||
| $modalRoot.find( '[data-id="section-confirm-delete-contract"]' ).first().hide(); | |||
| $modalRoot.find( '[data-id="section-loader"]' ).first().show(); | |||
| app.rpc.call( | |||
| 'Contract', | |||
| 'delete', | |||
| { | |||
| contractId : contractId | |||
| }, | |||
| function( resp ) | |||
| { | |||
| if ( resp && resp.hasOwnProperty( 'code' ) && resp.code == 200 ) | |||
| { | |||
| app.UI.toastSuccess( 'Vertrag wurde erfolgreich gelöscht.', 'Vertrag gelöscht' ); | |||
| if ( redirectPath ) | |||
| { | |||
| app.redirect( redirectPath ); | |||
| } | |||
| else | |||
| { | |||
| app.redirect( 'contract/list/' + groupId ); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| app.UI.toastError( 'Der Vertrag konnte nicht gelöscht werden, ' + | |||
| 'da er entweder mindestens eine verknüpfte Verrechnung oder bereits ' + | |||
| 'eine Einzahlung stattgefunden hat', | |||
| 'Fehler' ); | |||
| } | |||
| $modalRoot.modal( 'hide' ); | |||
| } | |||
| ); | |||
| }); | |||
| }); | |||
| $modalRoot.on( 'hidden.bs.modal', function() | |||
| { | |||
| $modalRoot.modal( 'dispose' ); | |||
| }); | |||
| $modalRoot.modal( 'show' ); | |||
| } | |||
| ); | |||
| } | |||
| $container.find( '.sk-loading' ).toggleClass( 'sk-loading' ); | |||
| }.bind ( this ) | |||
| ); | |||
| }, | |||
| destroy : function() | |||
| @@ -40,10 +40,62 @@ | |||
| href="javascript:history.back()"> | |||
| Abbrechen | |||
| </a> | |||
| <% if ( true === contractIsDeletable ) { %> | |||
| <button type="button" | |||
| class="btn btn-danger" | |||
| data-id="btnDeleteContract"> | |||
| Löschen | |||
| </button> | |||
| <% } %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="modal inmodal fade" | |||
| data-id="modal-confirm-delete-contract" | |||
| tabindex="-1" | |||
| role="dialog" | |||
| aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <button type="button" | |||
| class="close" | |||
| data-dismiss="modal"> | |||
| <span aria-hidden="true">×</span><span class="sr-only">Close</span> | |||
| </button> | |||
| <h4 class="modal-title">Vertrag löschen</h4> | |||
| </div> | |||
| <div class="modal-body"> | |||
| <section data-id="section-confirm-delete-contract" | |||
| style="display: none"> | |||
| <h5>Vertrag löschen?</h5> | |||
| <p> | |||
| Möchten Sie den Vertrag wirklich löschen? | |||
| </p> | |||
| </section> | |||
| <section data-id="section-loader" | |||
| style="display: none"> | |||
| <div style="height: 200px; padding-top: 70px"> | |||
| <div class="sk-spinner sk-spinner-double-bounce"> | |||
| <div class="sk-double-bounce1"></div> | |||
| <div class="sk-double-bounce2"></div> | |||
| </div> | |||
| </div> | |||
| </section> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" | |||
| class="btn btn-white" | |||
| data-dismiss="modal">Abbrechen</button> | |||
| <button type="button" | |||
| data-id="btnDelete" | |||
| class="btn btn-danger">Ja, löschen</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -118,6 +118,49 @@ | |||
| </select> | |||
| </div> | |||
| </div> | |||
| <div class="form-group row"> | |||
| <label class="col-sm-2 col-form-label"> | |||
| BLZ, IBAN | |||
| </label> | |||
| <div class="col-sm-3"> | |||
| <input type="text" | |||
| name="bank_code" | |||
| class="form-control" | |||
| value="<%= group.getContracteeData( 'bank_code' ) ? group.getContracteeData( 'bank_code' ) : '' %>" | |||
| placeholder="BLZ" /> | |||
| </div> | |||
| <div class="col-sm-7"> | |||
| <input type="text" | |||
| name="account_number" | |||
| class="form-control" | |||
| value="<%= group.getContracteeData( 'account_number' ) ? group.getContracteeData( 'account_number' ) : '' %>" | |||
| placeholder="IBAN" /> | |||
| </div> | |||
| </div> | |||
| <div class="form-group row"> | |||
| <label class="col-sm-2 col-form-label"> | |||
| Bankinstitut | |||
| </label> | |||
| <div class="col-sm-10"> | |||
| <input type="text" | |||
| name="bank_name" | |||
| class="form-control" | |||
| value="<%= group.getContracteeData( 'bank_name' ) ? group.getContracteeData( 'bank_name' ) : '' %>" | |||
| placeholder="Bankinstitut" /> | |||
| </div> | |||
| </div> | |||
| <div class="form-group row"> | |||
| <label class="col-sm-2 col-form-label"> | |||
| PaypalMe | |||
| </label> | |||
| <div class="col-sm-10"> | |||
| <input type="text" | |||
| name="paypal_me" | |||
| class="form-control" | |||
| value="<%= group.getContracteeData( 'paypal_me' ) ? group.getContracteeData( 'paypal_me' ) : '' %>" | |||
| placeholder="PaypalMe" /> | |||
| </div> | |||
| </div> | |||
| <div class="hr-line-dashed"></div> | |||
| </form> | |||
| </div> | |||
| @@ -151,7 +151,7 @@ class TB_Server_Control_Contract | |||
| { | |||
| $resp->addData( 'contractAppointments', [] ); | |||
| } | |||
| $resp->addData('contractIsDeletable', $contract->isDeletable()); | |||
| return $resp; | |||
| } | |||
| @@ -515,6 +515,32 @@ class TB_Server_Control_Contract | |||
| return $resp; | |||
| } | |||
| public static function delete( TB_Server_Core_RequestData $params ) | |||
| { | |||
| $resp = new TB_Server_Core_Response(); | |||
| $sessionProfile = TB_Server_Core_Session::get()->getProfile(); | |||
| $contractId = $params->get('contractId' ); | |||
| $contract = TB_Shared_Ent_TeamData_Contract::get( $contractId ); | |||
| if ( NULL === $contract ) | |||
| { | |||
| throw new \Exception( 'Contract not found.' ); | |||
| } | |||
| if ( false === $sessionProfile->isAdminOfTeam( $contract->team_id ) && $sessionProfile->id != $contract->profile_id ) | |||
| { | |||
| throw new \Exception('Profile is not allowed to delete contract'); | |||
| } | |||
| if ( !$contract->isDeletable() ) | |||
| { | |||
| throw new \Exception('Contract is not deletable'); | |||
| } | |||
| $contract->delete(); | |||
| return $resp; | |||
| } | |||
| public static function updateAttendances( TB_Server_Core_RequestData $params ) | |||
| { | |||
| $resp = new TB_Server_Core_Response(); | |||
| @@ -912,6 +912,11 @@ class TB_Server_Control_Team { | |||
| $cZipCode = $params->get( 'zip_code' ); | |||
| $cCity = $params->get( 'city' ); | |||
| $cCountry = $params->get( 'country' ); | |||
| $cBankCode = $params->get( 'bank_code' ); | |||
| $cAccountNumber = $params->get( 'account_number' ); | |||
| $cBankName = $params->get( 'bank_name' ); | |||
| $cPaypalMe = $params->get( 'paypal_me' ); | |||
| if ( !$cName || !$cStreet || !$cZipCode || !$cCity || !$cCountry ) | |||
| { | |||
| @@ -923,7 +928,11 @@ class TB_Server_Control_Team { | |||
| 'street' => $cStreet, | |||
| 'zip_code' => $cZipCode, | |||
| 'city' => $cCity, | |||
| 'country' => $cCountry | |||
| 'country' => $cCountry, | |||
| 'bank_code' => $cBankCode, | |||
| 'account_number' => $cAccountNumber, | |||
| 'bank_name' => $cBankName, | |||
| 'paypal_me' => $cPaypalMe | |||
| ); | |||
| $team->save(); | |||
| @@ -514,7 +514,7 @@ class TB_Shared_Ent_TeamData_Appointment extends Francis_Db_Row | |||
| public static function getTeamAppointmentsForAttendedProfileId( $teamId, $profileId ) | |||
| { | |||
| $sql = 'SELECT a.id AS `attendee_id`, a.contract_id AS `attendee_contract_id`, app.* FROM `attendee` AS a '; | |||
| $sql = 'SELECT a.id AS `attendee_id`, a.contract_id AS `attendee_contract_id`, a.`profile_child_id` AS `attendee_child_id`, app.* FROM `attendee` AS a '; | |||
| $sql .= 'LEFT JOIN appointment AS `app` ON a.appointment_id = app.id '; | |||
| $sql .= 'WHERE '; | |||
| $sql .= 'app.team_id = :team_id AND a.profile_id = :profile_id AND '; | |||
| @@ -218,6 +218,12 @@ class TB_Shared_Ent_TeamData_Contract extends Francis_Db_Row | |||
| TB_Shared_Ent_TeamData_Attendee::removeContractId( $this->id ); | |||
| } | |||
| public function isDeletable() | |||
| { | |||
| $attendees = TB_Shared_Ent_TeamData_Attendee::getAttendancesForContractId( $this->id ); | |||
| return (int) $this->price_payed === 0 && count($attendees) === 0; | |||
| } | |||
| /** | |||
| * Return table name | |||
| * | |||