Repository: techdev-solutions/trackr-backend Branch: development Commit: f985c5f16ee6 Files: 245 Total size: 553.9 KB Directory structure: gitextract_0tzlhsw6/ ├── .gitignore ├── LICENSE ├── README.md ├── application.yaml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src/ ├── main/ │ ├── java/ │ │ └── de/ │ │ └── techdev/ │ │ └── trackr/ │ │ ├── Trackr.java │ │ ├── core/ │ │ │ ├── mail/ │ │ │ │ ├── GMailConfiguration.java │ │ │ │ ├── MailService.java │ │ │ │ └── support/ │ │ │ │ ├── AsyncMailService.java │ │ │ │ └── NoOpJavaMailSender.java │ │ │ ├── pdf/ │ │ │ │ ├── HtmlPdfConverter.java │ │ │ │ ├── PdfCreationException.java │ │ │ │ ├── PdfRenderer.java │ │ │ │ └── ThymeleafRenderer.java │ │ │ ├── security/ │ │ │ │ ├── AuthorityService.java │ │ │ │ ├── InMemoryAuthorityService.java │ │ │ │ ├── InMemorySecurityConfiguration.java │ │ │ │ ├── MethodSecurityConfiguration.java │ │ │ │ ├── OAuth2AuthorityService.java │ │ │ │ └── OAuth2ResourceServerConfiguration.java │ │ │ └── web/ │ │ │ ├── api/ │ │ │ │ ├── ApiRepositoryRestConfiguration.java │ │ │ │ ├── ApiWebMvcConfiguration.java │ │ │ │ ├── ArgumentResolverPagingAndSortingTemplateVariables.java │ │ │ │ ├── ExceptionHandlers.java │ │ │ │ ├── JsonMappingHandlerExceptionResolver.java │ │ │ │ └── RepositoryEntityLinksWithoutProjection.java │ │ │ └── converters/ │ │ │ └── DateConverter.java │ │ ├── domain/ │ │ │ ├── ApiBeansConfiguration.java │ │ │ ├── common/ │ │ │ │ ├── EmployeeSettingsLocaleResolver.java │ │ │ │ ├── FederalState.java │ │ │ │ ├── FederalStateController.java │ │ │ │ └── UuidMapper.java │ │ │ ├── company/ │ │ │ │ ├── Address.java │ │ │ │ ├── AddressEventHandler.java │ │ │ │ ├── AddressRepository.java │ │ │ │ ├── Company.java │ │ │ │ ├── CompanyEventHandler.java │ │ │ │ ├── CompanyRepository.java │ │ │ │ ├── CompanyWithAddressAndContactPersonsProjection.java │ │ │ │ ├── ContactPerson.java │ │ │ │ ├── ContactPersonEventHandler.java │ │ │ │ └── ContactPersonRepository.java │ │ │ ├── employee/ │ │ │ │ ├── Employee.java │ │ │ │ ├── EmployeeController.java │ │ │ │ ├── EmployeeEventHandler.java │ │ │ │ ├── EmployeeRepository.java │ │ │ │ ├── EmployeeScheduledJob.java │ │ │ │ ├── Projections.java │ │ │ │ ├── SelfEmployee.java │ │ │ │ ├── SelfEmployeeRepository.java │ │ │ │ ├── Settings.java │ │ │ │ ├── SettingsRepository.java │ │ │ │ ├── addressbook/ │ │ │ │ │ ├── AddressBookController.java │ │ │ │ │ └── EmployeeForAddressBookDTO.java │ │ │ │ ├── expenses/ │ │ │ │ │ ├── TravelExpense.java │ │ │ │ │ ├── TravelExpenseEventHandler.java │ │ │ │ │ ├── TravelExpenseRepository.java │ │ │ │ │ ├── TravelExpenseTypeController.java │ │ │ │ │ └── reports/ │ │ │ │ │ ├── Projections.java │ │ │ │ │ ├── Report.java │ │ │ │ │ ├── ReportController.java │ │ │ │ │ ├── ReportEventHandler.java │ │ │ │ │ ├── ReportNotifyService.java │ │ │ │ │ ├── ReportRepository.java │ │ │ │ │ ├── ReportService.java │ │ │ │ │ └── comments/ │ │ │ │ │ ├── Comment.java │ │ │ │ │ ├── CommentEventHandler.java │ │ │ │ │ ├── CommentRepository.java │ │ │ │ │ └── CommentWithEmployeeProjection.java │ │ │ │ ├── login/ │ │ │ │ │ ├── PrincipalController.java │ │ │ │ │ └── support/ │ │ │ │ │ └── SupervisorService.java │ │ │ │ ├── sickdays/ │ │ │ │ │ ├── SickDays.java │ │ │ │ │ ├── SickDaysEventHandler.java │ │ │ │ │ ├── SickDaysNotifyService.java │ │ │ │ │ ├── SickDaysRepository.java │ │ │ │ │ └── SickDaysWithEmployeeProjection.java │ │ │ │ ├── vacation/ │ │ │ │ │ ├── Holiday.java │ │ │ │ │ ├── HolidayCalculator.java │ │ │ │ │ ├── HolidayRepository.java │ │ │ │ │ ├── VacationRequest.java │ │ │ │ │ ├── VacationRequestApproveService.java │ │ │ │ │ ├── VacationRequestController.java │ │ │ │ │ ├── VacationRequestEventHandler.java │ │ │ │ │ ├── VacationRequestRepository.java │ │ │ │ │ ├── VacationRequestScheduledJobs.java │ │ │ │ │ ├── VacationRequestWithEmployeeAndApproverProjection.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── MailApproveService.java │ │ │ │ │ ├── MessageWrapper.java │ │ │ │ │ ├── VacationRequestEmployeeToDaysTotalService.java │ │ │ │ │ └── VacationRequestNotifyService.java │ │ │ │ └── worktimetracking/ │ │ │ │ └── WorkTimeTrackingReminderService.java │ │ │ ├── project/ │ │ │ │ ├── Project.java │ │ │ │ ├── ProjectEventHandler.java │ │ │ │ ├── ProjectRepository.java │ │ │ │ ├── ProjectWithCompanyAndDebitorProjection.java │ │ │ │ ├── billtimes/ │ │ │ │ │ ├── BillableTime.java │ │ │ │ │ ├── BillableTimeController.java │ │ │ │ │ ├── BillableTimeEventHandler.java │ │ │ │ │ ├── BillableTimeRepository.java │ │ │ │ │ └── BillableTimeWithProjectProjection.java │ │ │ │ ├── invoice/ │ │ │ │ │ ├── ChangeStateService.java │ │ │ │ │ ├── Invoice.java │ │ │ │ │ ├── InvoiceController.java │ │ │ │ │ ├── InvoiceEventHandler.java │ │ │ │ │ ├── InvoiceOverdueService.java │ │ │ │ │ ├── InvoiceRepository.java │ │ │ │ │ ├── InvoiceScheduledJob.java │ │ │ │ │ └── InvoiceWithDebitorProjection.java │ │ │ │ └── worktimes/ │ │ │ │ ├── CustomWorkTime.java │ │ │ │ ├── Projections.java │ │ │ │ ├── WorkTime.java │ │ │ │ ├── WorkTimeController.java │ │ │ │ ├── WorkTimeEmployee.java │ │ │ │ ├── WorkTimeEventHandler.java │ │ │ │ └── WorkTimeRepository.java │ │ │ ├── scheduling/ │ │ │ │ ├── LastWorkdayDayOfMonthTrigger.java │ │ │ │ └── ScheduledJobsConfiguration.java │ │ │ ├── translations/ │ │ │ │ └── TranslationController.java │ │ │ └── validation/ │ │ │ ├── EndAfterBeginValidator.java │ │ │ ├── ProjectBelongsToCompanyValidator.java │ │ │ └── constraints/ │ │ │ ├── EndAfterBegin.java │ │ │ └── ProjectBelongsToCompany.java │ │ └── util/ │ │ └── LocalDateUtil.java │ └── resources/ │ ├── META-INF/ │ │ └── mail-integration.xml │ ├── banner.txt │ ├── data.sql │ ├── db/ │ │ └── migration/ │ │ ├── V10__modify_travel_expense_add_comment.sql │ │ ├── V11__add_travel_expense_report_comments.sql │ │ ├── V12__modify_company_add_time_for_payment.sql │ │ ├── V13__modify_travel_expense_reports_add_debitor_and_project.sql │ │ ├── V14__add_uuid_mapping.sql │ │ ├── V15__migrate_credentials.sql │ │ ├── V16__add_holidays_2015.sql │ │ ├── V17__add_holidays_2015.sql │ │ ├── V18__expense_paid_marker.sql │ │ ├── V19__add_employee_address.sql │ │ ├── V1__create_schema.sql │ │ ├── V20__add_employe_deleted.sql │ │ ├── V2__add_roles.sql │ │ ├── V3__add_holidays_2014.sql │ │ ├── V4__add_invoices.sql │ │ ├── V5__modify_vacationrequests.sql │ │ ├── V6__modify_travel_expense_reports_and_contact_persons.sql │ │ ├── V7__update_travel_expense_report_submission_date_from_travel_expenses.sql │ │ ├── V8__add_sick_days.sql │ │ └── V9__modify_travel_expense_report.sql │ ├── i18n/ │ │ ├── trackr-de.json │ │ ├── trackr-en.json │ │ └── validation/ │ │ ├── messages_de.properties │ │ └── messages_en.properties │ ├── logback-console.xml │ ├── logback-file.xml │ └── pdfTemplates/ │ └── travel-expenses/ │ └── report.html └── test/ ├── java/ │ └── de/ │ └── techdev/ │ ├── test/ │ │ ├── FlywayTest.java │ │ ├── InMemoryOAuth2ResourceServerConfiguration.java │ │ ├── TestConstants.java │ │ ├── TransactionalIntegrationTest.java │ │ ├── oauth/ │ │ │ ├── OAuthRequest.java │ │ │ └── OAuthTestExecutionListener.java │ │ └── rest/ │ │ ├── AbstractDomainResourceSecurityTest.java │ │ ├── AbstractJsonGenerator.java │ │ ├── AbstractRestIntegrationTest.java │ │ ├── DomainResourceTestMatchers.java │ │ └── TestRestTemplate.java │ └── trackr/ │ ├── core/ │ │ └── web/ │ │ └── converters/ │ │ └── DateConverterTest.java │ └── domain/ │ ├── common/ │ │ ├── FederalStateControllerIntegrationTest.java │ │ ├── UuidMapperIntegrationTest.java │ │ └── UuidMapperTest.java │ ├── company/ │ │ ├── AddressJsonGenerator.java │ │ ├── AddressResourceSecurityTest.java │ │ ├── CompanyJsonGenerator.java │ │ ├── CompanyRepositoryTest.java │ │ ├── CompanyResourceSecurityTest.java │ │ ├── ContactPersonJsonGenerator.java │ │ └── ContactPersonResourceSecurityTest.java │ ├── employee/ │ │ ├── EmployeeControllerSecurityTest.java │ │ ├── EmployeeJsonGenerator.java │ │ ├── EmployeeResourceIntegrationTest.java │ │ ├── EmployeeResourceSecurityTest.java │ │ ├── SelfEmployeeRepositoryTest.java │ │ ├── addressbook/ │ │ │ ├── AddressBookControllerSecurityTest.java │ │ │ └── AddressBookControllerTest.java │ │ ├── expenses/ │ │ │ ├── TravelExpenseJsonGenerator.java │ │ │ ├── TravelExpenseResourceSecurityTest.java │ │ │ └── report/ │ │ │ ├── ReportJsonGenerator.java │ │ │ ├── ReportResourceSecurityTest.java │ │ │ ├── ReportServiceTest.java │ │ │ └── comment/ │ │ │ ├── CommentJsonGenerator.java │ │ │ └── CommentResourceSecurityTest.java │ │ ├── login/ │ │ │ └── PrincipalControllerSecurityTest.java │ │ ├── sickdays/ │ │ │ ├── SickDaysJsonGenerator.java │ │ │ └── SickDaysResourceSecurityTest.java │ │ ├── vacation/ │ │ │ ├── HolidayCalculatorTest.java │ │ │ ├── HolidayResourceTest.java │ │ │ ├── VacationRequestControllerSecurityTest.java │ │ │ ├── VacationRequestJsonGenerator.java │ │ │ ├── VacationRequestRepositoryTest.java │ │ │ ├── VacationRequestResourceSecurityTest.java │ │ │ ├── VacationRequestScheduledJobsTest.java │ │ │ └── support/ │ │ │ ├── MailApproveServiceTest.java │ │ │ ├── MessageWrapperTest.java │ │ │ ├── VacationRequestEmployeeToDaysTotalServiceTest.java │ │ │ └── VacationRequestNotifyServiceTest.java │ │ └── worktimetracking/ │ │ └── WorkTimeTrackingReminderServiceIntegrationTest.java │ ├── project/ │ │ ├── ProjectJsonGenerator.java │ │ ├── ProjectResourceSecurityTest.java │ │ ├── billtimes/ │ │ │ ├── BillableTimeControllerIntegrationTest.java │ │ │ ├── BillableTimeResourceSecurityTest.java │ │ │ └── BillableTimesJsonGenerator.java │ │ ├── invoice/ │ │ │ ├── InvoiceEventHandlerTest.java │ │ │ ├── InvoiceJsonGenerator.java │ │ │ └── InvoiceResourceSecurityTest.java │ │ └── worktimes/ │ │ ├── CustomWorkTimeTest.java │ │ ├── WorkTimeControllerSecurityTest.java │ │ ├── WorkTimeControllerTest.java │ │ ├── WorkTimeJsonGenerator.java │ │ ├── WorkTimeRepositoryTest.java │ │ └── WorkTimeResourceSecurityTest.java │ ├── scheduling/ │ │ └── LastWorkdayDayOfMonthTriggerTest.java │ └── translations/ │ ├── TranslationControllerSecurityTest.java │ └── TranslationControllerTest.java └── resources/ └── de/ └── techdev/ └── trackr/ └── domain/ ├── company/ │ ├── address/ │ │ └── resourceTest.sql │ ├── contactPerson/ │ │ └── resourceTest.sql │ ├── repositoryTest.sql │ └── resourceTest.sql ├── employee/ │ ├── expenses/ │ │ ├── report/ │ │ │ ├── comment/ │ │ │ │ └── resourceTest.sql │ │ │ └── resourceTest.sql │ │ └── resourceTest.sql │ ├── login/ │ │ └── resourceTest.sql │ ├── resourceTest.sql │ ├── sickdays/ │ │ └── resourceTest.sql │ └── vacation/ │ ├── holiday/ │ │ └── resourceTest.sql │ ├── repositoryTest.sql │ └── resourceTest.sql ├── emptyDatabase.sql ├── project/ │ ├── billtimes/ │ │ └── resourceTest.sql │ ├── invoice/ │ │ └── resourceTest.sql │ ├── resourceTest.sql │ └── worktimes/ │ ├── repositoryTest.sql │ └── resourceTest.sql └── tableUuidMapping.sql ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # OS application.pid # gradle / Maven target/ build/ .gradle/ # IDEA .idea/ *.iml *.ipr atlassian-ide-plugin.xml # Eclipse .settings/ .project # OSX .DS_Store /.nb-gradle/private/ ================================================ FILE: LICENSE ================================================ The MIT License Copyright (c) 2014 techdev Solutions UG http://techdev.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ trackr backend ============== What is it? ------------- trackr is an application to track petty much everything that is going on in your company. Keep track of vacations, sick days, invoices and many more. trackr comes with a Java-based backend and a [frontend](https://github.com/techdev-solutions/trackr-frontend) written in AngularJS. This project is the Java/Spring based backend, a stateless REST API with either OAuth2 or basic authentication. You can read all about trackr in our developer blog: * [Architecture and Backend](http://blog.techdev.de/trackr-an-angularjs-app-with-a-java-8-backend-part-i/) * [Testing](http://blog.techdev.de/testing-a-secured-spring-data-rest-service-with-java-8-and-mockmvc/) * [Mail Approvals with Spring Integration](http://blog.techdev.de/mail-approvals-with-spring-integration/) * [Frontend](http://blog.techdev.de/trackr-an-angularjs-app-with-a-java-8-backend-part-ii/) * [File Downloads with AngularJS](http://blog.techdev.de/an-angularjs-directive-to-download-pdf-files/) * [Processes and Tools](http://blog.techdev.de/trackr-an-angularjs-app-with-a-java-8-backend-part-iii/) For the API documentation just go [here](http://techdev-solutions.github.io/trackr-api-documentation/getting_started.html). There is also a [Vagrant](https://www.vagrantup.com/) project building the whole application over [here](https://github.com/techdev-solutions/trackr-vagrant). How to start ------------ If you just want to mess around with the API a bit the default configuration is very sensible and has no external dependencies (well, except Java). If you have gradle, just run gradle run If you don't have gradle and want to use the wrapper run ./gradlew run # or gradlew.bat run If you want to start from your IDE, i.e. for debugging open the class `Trackr` and start the main method. To verify it works you can use curl. The users don't have a password in this configuration, so just press enter when curl asks for one. If you don't like the usernames change them in import.sql. curl --user moritz.schulze@techdev.de localhost:8080 The default config uses port 8080, if that is used on your system you can add server: port: $port to the top of the application.yaml and choose a port that you want for `$port`. Profiles -------- trackr has a lot of Spring profiles to add/switch features. | profile | description | notes | |--------------------|------------------------------------------------------------|------------------------------------------------------------------| | in-memory-database | uses a H2 database, creates the schema with hibernate | excluse with real-database | | real-database | uses a configurable database, executes flyway | exclusive with in-memory-database | | http-basic | protects the API with HTTP basic authentication | exclusive with oauth | | oauth | protects the API as a OAuth2 resource server | exclusive with http-basic. Database for OAuth2 tokens needed. | | granular-security | roles and per endpoint security | | | gmail | sends mail with Gmail and enables mail receiving | when off, does not receive mails and uses a logging mail sender. | | dev | initialize the database with data.sql | | | prod | Just some different settings for our production env | | Take a look in the application.yaml to see what properties these profiles need. The default profiles are `in-memory-database,dev,granular-security,http-basic`. If you want to use other profiles, there are several possible ways. 1. You can change the `spring.profiles.active` value in application.yaml 2. If you use `gradle run` you can prepend (example) `SPRING_PROFILES_ACTIVE=dev,gmail,real-database`. You can also use this to overwrite e.g. the port with `SERVER_PORT=8000`. 3. If you run from your IDE, you can add `--spring.profiles.active=dev,gmail,real-database` as program arguments to the run configuration. Please refer to the [Spring Boot Reference](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) for more information. ### The oauth profile The oauth profile marks the trackr backend as a OAuth2 resource server, that means access is only possible with a valid access token issued by an authorization server. We use a JDBC token store, so valid tokens need to be put there. Please take a look at our (soon to be open sourced) techdev portal to see how we do this. ### The granular-security profile When this is not selected, to access the API the user needs to be authenticated. With granular security the access to some endpoints depend on the role of the user or even the id of the user. In trackr, the id of a user is the email address of the belonging employee. When the oauth profile is switched off, all users have the role ROLE_ADMIN. When oauth is on, the roles must be stored in the access token. Take a look at the `@PreAuthorize` and `@PostAuthorize` annotations in the code to see what this will activate. How to build ------------ Just run gradle build (or use the wrapper if you don't have gradle installed). The JAR file will be in `build/libs` and can just be run with `java -jar`. The application.yaml file has to be in the working directory where the `java` command was issued. ================================================ FILE: application.yaml ================================================ spring: jpa: hibernate: naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl datasource: initialize: false profiles: active: in-memory-database,dev,granular-security,http-basic trackr: frontendUrl: http://localhost logging: config: classpath:logback-console.xml --- spring: profiles: prod trackr: frontendUrl: logging: config: classpath:logback-file.xml --- spring: profiles: dev datasource: initialize: true --- spring: profiles: real-database jpa: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect ddl-auto: validate datasource: driverClassName: org.postgresql.Driver url: jdbc:postgresql://127.0.0.1:5432/trackr username: password: flyway: schemas: public --- spring: profiles: in-memory-database datasource: driverClassName: org.h2.Driver url: jdbc:h2:mem:trackr;DB_CLOSE_DELAY\=-1 username: sa password: jpa: hibernate: dialect: org.hibernate.dialect.H2Dialect ddl-auto: create flyway: enabled: false --- spring: profiles: oauth trackr: database: oauth: driverClassName: org.postgresql.Driver url: jdbc:postgresql://127.0.0.1:5432/techdev_oauth username: password: --- spring: profiles: gmail mail: host: smtp.gmail.com port: 465 username: password: properties: mail.smtp.auth: true mail.smtp.starttls.enable: true mail.transport.protocol: smtp mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory ================================================ FILE: build.gradle ================================================ plugins { id 'org.springframework.boot' version '1.5.19.RELEASE' } apply plugin: 'java' apply plugin: 'jacoco' sourceCompatibility = 1.8 compileJava.options.encoding = 'UTF-8' // Options for naming the JAR file archivesBaseName = 'trackr-backend' version = '1.0' if(project.hasProperty('teamcity')) { version += '-build-' + project.teamcity['build.number'] } else { version += '-localbuild' } repositories { mavenCentral() } springBoot { executable = true } dependencies { compile "org.springframework.boot:spring-boot-starter-data-rest" compile "org.springframework.boot:spring-boot-starter-data-jpa" compile "org.springframework.boot:spring-boot-starter-mail" compile "org.springframework.boot:spring-boot-starter-integration" compile "org.springframework.boot:spring-boot-starter-security" // not included in boot compile "org.springframework.integration:spring-integration-mail:4.2.5.RELEASE" compile "org.springframework.security.oauth:spring-security-oauth2" compile "com.h2database:h2" compile "org.postgresql:postgresql" compile "org.flywaydb:flyway-core" compile("org.xhtmlrenderer:flying-saucer-pdf-itext5:9.0.6") compile("org.thymeleaf:thymeleaf") compileOnly "org.projectlombok:lombok:1.12.4" testCompileOnly "org.projectlombok:lombok:1.12.4" annotationProcessor "org.projectlombok:lombok:1.12.4" testAnnotationProcessor "org.projectlombok:lombok:1.12.4" compile "org.glassfish:javax.json:1.0" // Not included by default for Spring Boot 1.5 compile "commons-io:commons-io:2.1" testCompile "org.springframework.boot:spring-boot-starter-test" testCompile("org.echocat.jomon:testing:1.4.3") { exclude group: "org.mockito" } testCompile "org.mockito:mockito-core:1.9.5" testCompile "com.jayway.jsonpath:json-path" testCompile "org.apache.httpcomponents:httpclient" } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Jan 23 09:44:57 CET 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-bin.zip ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: src/main/java/de/techdev/trackr/Trackr.java ================================================ package de.techdev.trackr; import org.springframework.boot.ApplicationPid; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.Primary; import java.io.File; import java.io.IOException; import javax.annotation.PostConstruct; import javax.sql.DataSource; @SpringBootApplication(exclude = MailSenderAutoConfiguration.class) @ImportResource(value = "classpath:META-INF/mail-integration.xml") public class Trackr { @Bean @Primary @ConfigurationProperties("spring.datasource") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @PostConstruct private void handlePid() throws IOException { File file = new File("application.pid"); new ApplicationPid().write(file); file.deleteOnExit(); } public static void main(String[] args) { SpringApplication.run(Trackr.class, args); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/mail/GMailConfiguration.java ================================================ package de.techdev.trackr.core.mail; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.mail.MailProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.mail.javamail.JavaMailSenderImpl; import java.util.Map; import java.util.Properties; import javax.mail.Session; /** * This configuration class is mostly copied from {@link org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration}. * * Since we need to expose the {@link #mailSession()} for our {@code mail-integration.xml} to work and the Spring autoconfig * class creates a circular dependency (JavaMailSender <-> Session) we have to use the relevant parts ourselves. */ @Configuration @EnableConfigurationProperties(MailProperties.class) @Profile("gmail") public class GMailConfiguration { @Autowired private MailProperties properties; @Bean public JavaMailSenderImpl mailSender() { JavaMailSenderImpl sender = new JavaMailSenderImpl(); applyProperties(sender); return sender; } private void applyProperties(JavaMailSenderImpl sender) { sender.setHost(this.properties.getHost()); if (this.properties.getPort() != null) { sender.setPort(this.properties.getPort()); } sender.setUsername(this.properties.getUsername()); sender.setPassword(this.properties.getPassword()); sender.setProtocol(this.properties.getProtocol()); if (this.properties.getDefaultEncoding() != null) { sender.setDefaultEncoding(this.properties.getDefaultEncoding().name()); } if (!this.properties.getProperties().isEmpty()) { sender.setJavaMailProperties(asProperties(this.properties.getProperties())); } } private Properties asProperties(Map source) { Properties properties = new Properties(); properties.putAll(source); return properties; } /** * We expose the mail session as a bean for the Spring Integration mail receiver. */ @Bean public Session mailSession() { return mailSender().getSession(); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/mail/MailService.java ================================================ package de.techdev.trackr.core.mail; import org.springframework.mail.SimpleMailMessage; /** * @author Moritz Schulze */ public interface MailService { void sendMail(SimpleMailMessage mailMessage); } ================================================ FILE: src/main/java/de/techdev/trackr/core/mail/support/AsyncMailService.java ================================================ package de.techdev.trackr.core.mail.support; import de.techdev.trackr.core.mail.MailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.mail.MailMessage; import org.springframework.mail.SimpleMailMessage; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.GenericMessage; import org.springframework.stereotype.Component; /** * Puts the mail message into a queue to be sent by Spring Integration. */ @Component public class AsyncMailService implements MailService { @Autowired @Qualifier("mailSendChannel") private MessageChannel mailChannel; @Override public void sendMail(SimpleMailMessage mailMessage) { Message asyncMessage = new GenericMessage<>(mailMessage); mailChannel.send(asyncMessage); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/mail/support/NoOpJavaMailSender.java ================================================ package de.techdev.trackr.core.mail.support; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessagePreparator; import org.springframework.stereotype.Component; import javax.mail.internet.MimeMessage; import java.io.InputStream; import java.util.Arrays; @Slf4j @Profile("!gmail") @Component("mailSender") public class NoOpJavaMailSender implements JavaMailSender { @Override public MimeMessage createMimeMessage() { log.debug("Create mime message"); return null; } @Override public MimeMessage createMimeMessage(InputStream contentStream) { log.debug("Create Mime message with stream"); return null; } @Override public void send(MimeMessage mimeMessage) { log.debug("Send single mime message"); } @Override public void send(MimeMessage[] mimeMessages) { log.debug("Send multiple mime messages"); } @Override public void send(MimeMessagePreparator mimeMessagePreparator) { log.debug("Send mime message preparator"); } @Override public void send(MimeMessagePreparator[] mimeMessagePreparators) { log.debug("Send multiple mime message preparators"); } @Override public void send(SimpleMailMessage simpleMessage) { StringBuilder builder = new StringBuilder("Send simple mail message..\n"); builder .append("From: ").append(simpleMessage.getFrom()).append("\n") .append("To: ").append(Arrays.toString(simpleMessage.getTo())).append("\n") .append("Subject: ").append(simpleMessage.getSubject()).append("\n") .append("--------------------------------------------").append("\n") .append(simpleMessage.getText()); log.debug(builder.toString()); } @Override public void send(SimpleMailMessage[] simpleMessages) { log.debug("Send multiple simple messages"); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/pdf/HtmlPdfConverter.java ================================================ package de.techdev.trackr.core.pdf; import com.itextpdf.text.DocumentException; import org.xhtmlrenderer.pdf.ITextRenderer; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * @author Moritz Schulze */ public class HtmlPdfConverter { private ITextRenderer renderer; public HtmlPdfConverter() { renderer = new ITextRenderer(); //This can be used to set a font? // ITextFontResolver fontResolver = renderer.getFontResolver(); // ClassPathResource regular = new ClassPathResource("/META-INF/fonts/LiberationSerif-Regular.ttf"); // fontResolver.addFont(regular.getURL().toString(), BaseFont.IDENTITY_H, true); } public byte[] renderHtmlToPdf(String htmlContent) throws PdfCreationException { renderer.setDocumentFromString(htmlContent); renderer.layout(); byte[] pdfAsByteArray; try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) { renderer.createPDF(bos); pdfAsByteArray = bos.toByteArray(); } catch (DocumentException | IOException e) { throw new PdfCreationException(e); } return pdfAsByteArray; } } ================================================ FILE: src/main/java/de/techdev/trackr/core/pdf/PdfCreationException.java ================================================ package de.techdev.trackr.core.pdf; /** * @author Moritz Schulze */ public class PdfCreationException extends Exception { public PdfCreationException(Exception e) { super(e); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/pdf/PdfRenderer.java ================================================ package de.techdev.trackr.core.pdf; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.thymeleaf.context.Context; import javax.annotation.PostConstruct; /** * @author Moritz Schulze */ @Slf4j public class PdfRenderer { // @Value("${pdf.templatePath:META-INF/pdfTemplates}") @Value("pdfTemplates/") private String templatePath; // @Value("${pdf.templateSuffix:pdf.templateSuffix:.html}") @Value(".html") private String templateSuffix; private ThymeleafRenderer thymeleafRenderer; private HtmlPdfConverter htmlPdfConverter; public PdfRenderer() { htmlPdfConverter = new HtmlPdfConverter(); } @PostConstruct public void setUpRenderer() { thymeleafRenderer = new ThymeleafRenderer(templatePath, templateSuffix); } public byte[] renderPdf(String templateName, Context context) throws PdfCreationException { log.debug("Rendering template {}", templateName); String htmlContent = thymeleafRenderer.renderTemplateToHtml(templateName, context); return htmlPdfConverter.renderHtmlToPdf(htmlContent); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/pdf/ThymeleafRenderer.java ================================================ package de.techdev.trackr.core.pdf; import lombok.extern.slf4j.Slf4j; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; /** * Renders a thymeleaf template as a String containing HTML. * * @author Moritz Schulze */ @Slf4j public class ThymeleafRenderer { private TemplateEngine templateEngine; private String templatePath; private String templateSuffix; public ThymeleafRenderer(String templatePath, String templateSuffix) { this.templatePath = templatePath; this.templateSuffix = templateSuffix; setUpTemplateEngine(); } private void setUpTemplateEngine() { ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); log.debug("Configuring template resolver with: path {}, suffix {}", templatePath, templateSuffix); templateResolver.setPrefix(templatePath); templateResolver.setSuffix(templateSuffix); templateResolver.setTemplateMode("XHTML"); templateResolver.setCharacterEncoding("UTF-8"); this.templateEngine = new TemplateEngine(); this.templateEngine.setTemplateResolver(templateResolver); } /** * Render the given template to HTML with the context. * * @param templateName The template to render. * @param context The context to use when rendering the template. * @return The HTML content of the rendered template as a String. */ public String renderTemplateToHtml(String templateName, Context context) { return templateEngine.process(templateName, context); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/security/AuthorityService.java ================================================ package de.techdev.trackr.core.security; import java.util.Collection; /** * Access GrantedAuthorities for Employees and emails by GrantedAuthorities */ public interface AuthorityService { /** * Get all email addresses from employees who have the given authority. */ Collection getEmployeeEmailsByAuthority(String authority); } ================================================ FILE: src/main/java/de/techdev/trackr/core/security/InMemoryAuthorityService.java ================================================ package de.techdev.trackr.core.security; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import java.util.Collection; import java.util.stream.Collectors; @Service @Profile("!oauth") public class InMemoryAuthorityService implements AuthorityService { @Autowired private EmployeeRepository employeeRepository; @Override public Collection getEmployeeEmailsByAuthority(String authority) { return employeeRepository.findAllForAddressBook(new PageRequest(0, 100)).getContent().stream().map(Employee::getEmail).collect(Collectors.toList()); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/security/InMemorySecurityConfiguration.java ================================================ package de.techdev.trackr.core.security; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import static java.util.Arrays.asList; /** * This configuration enables HTTP Basic authentication and uses the employees as credentials, all with no password and admin access. * * It is only enabled when the oauth profile is off. */ @EnableWebSecurity @Configuration @Profile("http-basic") public class InMemorySecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private EmployeeRepository employeeRepository; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(username -> { Employee employee = employeeRepository.findByEmail(username); if (employee == null) { throw new BadCredentialsException("User not found"); } return new User(username, "", asList(new SimpleGrantedAuthority("ROLE_ADMIN"))); } ); } @Override protected void configure(HttpSecurity http) throws Exception { http. authorizeRequests().anyRequest().fullyAuthenticated() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .httpBasic() .realmName("trackr development realm") .and() .csrf().disable(); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/security/MethodSecurityConfiguration.java ================================================ package de.techdev.trackr.core.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.access.vote.RoleHierarchyVoter; import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; /** * This class enables global method security via {@link org.springframework.security.access.prepost.PreAuthorize} in JPA Repositories * and other controllers. It also enables the role hierarchy in them. */ @Profile("granular-security") @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { @Autowired private ApplicationContext applicationContext; /** * This is needed so {@link org.springframework.security.access.prepost.PreAuthorize} and so on know the role hierarchy. */ @Override protected MethodSecurityExpressionHandler createExpressionHandler() { DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler(); methodSecurityExpressionHandler.setRoleHierarchy(roleHierarchy()); //Needs to be done so we can access beans in security expressions methodSecurityExpressionHandler.setApplicationContext(applicationContext); return methodSecurityExpressionHandler; } @Bean public RoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); //TODO make this configurable roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_SUPERVISOR ROLE_SUPERVISOR > ROLE_EMPLOYEE ROLE_EMPLOYEE > ROLE_ANONYMOUS"); return roleHierarchy; } @Bean public RoleVoter roleVoter() { return new RoleHierarchyVoter(roleHierarchy()); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/security/OAuth2AuthorityService.java ================================================ package de.techdev.trackr.core.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import javax.sql.DataSource; import java.util.Collection; @Service @Profile("oauth") public class OAuth2AuthorityService implements AuthorityService { private JdbcTemplate jdbcTemplate; @Autowired public OAuth2AuthorityService(@Qualifier("oauthDataSource") DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); } @Override public Collection getEmployeeEmailsByAuthority(String authority) { return jdbcTemplate.queryForList("SELECT username FROM authorities WHERE authority = ?", String.class, authority); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/security/OAuth2ResourceServerConfiguration.java ================================================ package de.techdev.trackr.core.security; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import javax.sql.DataSource; import java.util.Collections; @Profile("oauth") @EnableWebSecurity @Configuration @EnableResourceServer public class OAuth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter { private static final String TRACKR_RESOURCE_ID = "techdev-services"; @Bean @Qualifier("oauthDataSource") @ConfigurationProperties(prefix = "trackr.database.oauth") public DataSource oauthDataSource() { return DataSourceBuilder.create().build(); } @Bean public TokenStore tokenStore() { return new JdbcTokenStore(oauthDataSource()); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId(TRACKR_RESOURCE_ID).tokenStore(tokenStore()); } @Override public void configure(HttpSecurity http) throws Exception { User anonymousUser = new User("anonymous", "EMPTY", Collections.singletonList(new SimpleGrantedAuthority("ROLE_ANONYMOUS"))); http .anonymous() .principal(anonymousUser) .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // since this is just a REST API we don't need a state. .and().requestMatchers().antMatchers("/**") .and().authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')") .antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')") .antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')") .antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')") .antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')"); http.headers().addHeaderWriter((request, response) -> { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); if (request.getMethod().equals("OPTIONS")) { response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers")); } }); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/api/ApiRepositoryRestConfiguration.java ================================================ package de.techdev.trackr.core.web.api; import de.techdev.trackr.core.web.converters.DateConverter; import de.techdev.trackr.domain.company.Address; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.domain.company.ContactPerson; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.expenses.TravelExpense; import de.techdev.trackr.domain.employee.expenses.reports.Report; import de.techdev.trackr.domain.employee.expenses.reports.comments.Comment; import de.techdev.trackr.domain.employee.sickdays.SickDays; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import de.techdev.trackr.domain.project.Project; import de.techdev.trackr.domain.project.billtimes.BillableTime; import de.techdev.trackr.domain.project.invoice.Invoice; import de.techdev.trackr.domain.project.worktimes.WorkTime; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @Configuration public class ApiRepositoryRestConfiguration extends RepositoryRestConfigurerAdapter { @Override public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.exposeIdsFor(Employee.class, Company.class, ContactPerson.class, Address.class, Project.class, WorkTime.class, BillableTime.class, VacationRequest.class, TravelExpense.class, Report.class, Comment.class, Invoice.class, SickDays.class); config.setReturnBodyOnUpdate(true); config.setReturnBodyOnCreate(true); } @Override public void configureConversionService(ConfigurableConversionService conversionService) { super.configureConversionService(conversionService); conversionService.addConverter(dateConverter()); } @Bean public DateConverter dateConverter() { return new DateConverter(); } /** * Add the validator to spring data rest. */ @Override public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) { validatingListener.addValidator("beforeSave", validator()); validatingListener.addValidator("beforeCreate", validator()); } /** * Custom validator that extracts messages with locale. Used by spring-data-rest. */ @Bean public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setValidationMessageSource(messageSource()); return localValidatorFactoryBean; } /** * Load all messages, reload when locale changes. */ @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasenames("classpath:org/hibernate/validator/ValidationMessages", "classpath:/i18n/validation/messages"); return messageSource; } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/api/ApiWebMvcConfiguration.java ================================================ package de.techdev.trackr.core.web.api; import de.techdev.trackr.core.web.converters.DateConverter; import de.techdev.trackr.domain.common.EmployeeSettingsLocaleResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.LocaleResolver; import java.util.List; @Configuration public class ApiWebMvcConfiguration extends RepositoryRestMvcConfiguration { @Autowired private DateConverter dateConverter; /** * The self HREF for entities contains {?projection} to indicate the parameter is available, but we don't want that. * The {@link RepositoryEntityLinksWithoutProjection} switches {?projection} off. */ @Override @Bean public RepositoryEntityLinks entityLinks() { ArgumentResolverPagingAndSortingTemplateVariables variables = new ArgumentResolverPagingAndSortingTemplateVariables(this.pageableResolver(), this.sortResolver()); return new RepositoryEntityLinksWithoutProjection(repositories(), resourceMappings(), config(), variables, backendIdConverterRegistry()); } @Bean public LocaleResolver localeResolver() { return new EmployeeSettingsLocaleResolver(); } @Override public void addFormatters(FormatterRegistry registry) { // super call is needed so the DomainClassConverter is used for linked resources // (e.g. companies/0/address). The super class will configure the DomainClassConverter correctly only with this // super call. super.addFormatters(registry); // We need to add the converter so non-Spring-Data-REST calls also use it. registry.addConverter(dateConverter); } /** * Needed for mapping exceptions in jackson that otherwise would get an ugly error message. */ @Bean public JsonMappingHandlerExceptionResolver jsonMappingHandlerExceptionResolver() { return new JsonMappingHandlerExceptionResolver(); } @Override public void extendHandlerExceptionResolvers(List exceptionResolvers) { exceptionResolvers.add(jsonMappingHandlerExceptionResolver()); super.extendHandlerExceptionResolvers(exceptionResolvers); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/api/ArgumentResolverPagingAndSortingTemplateVariables.java ================================================ package de.techdev.trackr.core.web.api; import org.springframework.core.MethodParameter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.rest.webmvc.support.PagingAndSortingTemplateVariables; import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver; import org.springframework.data.web.HateoasSortHandlerMethodArgumentResolver; import org.springframework.hateoas.TemplateVariables; import org.springframework.util.Assert; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Copy from Spring because thankfully they made it package protected. Nice! * * @deprecated See {@link RepositoryEntityLinksWithoutProjection} */ @Deprecated class ArgumentResolverPagingAndSortingTemplateVariables implements PagingAndSortingTemplateVariables { private static final Set> SUPPORTED_TYPES = Collections.unmodifiableSet(new HashSet(Arrays.asList(new Class[]{Pageable.class, Sort.class}))); private final HateoasPageableHandlerMethodArgumentResolver pagingResolver; private final HateoasSortHandlerMethodArgumentResolver sortResolver; public ArgumentResolverPagingAndSortingTemplateVariables(HateoasPageableHandlerMethodArgumentResolver pagingResolver, HateoasSortHandlerMethodArgumentResolver sortResolver) { Assert.notNull(pagingResolver, "HateoasPageableHandlerMethodArgumentResolver must not be null!"); Assert.notNull(sortResolver, "HateoasSortHandlerMethodArgumentResolver must not be null!"); this.pagingResolver = pagingResolver; this.sortResolver = sortResolver; } public TemplateVariables getPaginationTemplateVariables(MethodParameter parameter, UriComponents components) { return this.pagingResolver.getPaginationTemplateVariables(parameter, components); } public TemplateVariables getSortTemplateVariables(MethodParameter parameter, UriComponents template) { return this.sortResolver.getSortTemplateVariables(parameter, template); } public void enhance(UriComponentsBuilder builder, MethodParameter parameter, Object value) { if(value instanceof Pageable) { this.pagingResolver.enhance(builder, parameter, value); } else if(value instanceof Sort) { this.sortResolver.enhance(builder, parameter, value); } } public boolean supportsParameter(MethodParameter parameter) { return SUPPORTED_TYPES.contains(parameter.getParameterType()); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/api/ExceptionHandlers.java ================================================ package de.techdev.trackr.core.web.api; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.data.rest.core.RepositoryConstraintViolationException; import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage; import org.springframework.orm.jpa.JpaSystemException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.CONFLICT; /** * @author Moritz Schulze */ @ControllerAdvice @Slf4j public class ExceptionHandlers { private final MessageSourceAccessor messageSourceAccessor; @Autowired public ExceptionHandlers(MessageSource messageSource) { this.messageSourceAccessor = new MessageSourceAccessor(messageSource); } /** * This exception handler should handle violations of unique constraints. * TODO: JpaSystemException is really broad, we need to check somehow that this only gets invoked for unique constraint violations * @param e The exception to handle * @return An error message */ @ExceptionHandler(JpaSystemException.class) @ResponseBody @ResponseStatus(CONFLICT) public String handleJpaSystemException(JpaSystemException e) { return e.getMostSpecificCause().getMessage(); } /** * This is for custom controllers (i.e. not spring-data-rest) that do validation. * @param ex The {@link org.springframework.data.rest.core.RepositoryConstraintViolationException} thrown by the controller. * @return A map of fieldnames to FieldErrors */ @ResponseBody @ResponseStatus(BAD_REQUEST) @ExceptionHandler(RepositoryConstraintViolationException.class) public RepositoryConstraintViolationExceptionMessage handleRepositoryConstraintViolationException(RepositoryConstraintViolationException ex) { return new RepositoryConstraintViolationExceptionMessage(ex, messageSourceAccessor); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/api/JsonMappingHandlerExceptionResolver.java ================================================ package de.techdev.trackr.core.web.api; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.json.Json; import javax.json.stream.JsonGenerator; import javax.json.stream.JsonGeneratorFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.Writer; import static java.util.stream.Collectors.reducing; /** * Custom exception handler for exceptions thrown while JSON mapping by Jackson. They appear not to be handled by {@link org.springframework.web.bind.annotation.ControllerAdvice} * exception handlers. * * @author Moritz Schulze */ public class JsonMappingHandlerExceptionResolver implements HandlerExceptionResolver { protected JsonGeneratorFactory jsonGeneratorFactory; public JsonMappingHandlerExceptionResolver() { jsonGeneratorFactory = Json.createGeneratorFactory(null); } @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (HttpMessageNotReadableException.class.isAssignableFrom(ex.getClass()) && ex.getCause() != null && InvalidFormatException.class.isAssignableFrom(ex.getCause().getClass())) { Writer outputWriter; try { outputWriter = response.getWriter(); } catch (IOException e) { throw new IllegalStateException("Could not open response writer", e); } response.setStatus(HttpStatus.BAD_REQUEST.value()); response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE); writeExceptionAsJsonToOutput((InvalidFormatException) ex.getCause(), outputWriter); try { outputWriter.flush(); outputWriter.close(); } catch (IOException e) { throw new IllegalStateException("Could not flush and close response writer", e); } return new ModelAndView(); } return null; } /** * Writes the information in the exception as a JSON object like this: { "employee.salary": { "defaultMessage": "Cannot construct float from this value"}} * to a writer. * * @param ex The exception thrown by Jackson * @param writer The output writer to write to. */ protected void writeExceptionAsJsonToOutput(InvalidFormatException ex, Writer writer) { JsonGenerator jsonGenerator = jsonGeneratorFactory.createGenerator(writer); jsonGenerator.writeStartObject() .writeStartArray("errors") .writeStartObject() .write("property", getFieldPath(ex)) .write("message", ex.getOriginalMessage()) .writeEnd() //object in array .writeEnd() //array .writeEnd().close(); } /** * Appends all fieldNames in the path of the exception to a string connected with dots. * * @param ex The exception containing the path information * @return E.g. "employee.firstName" */ private String getFieldPath(InvalidFormatException ex) { return ex.getPath().stream().collect( reducing(null, //start value JsonMappingException.Reference::getFieldName, //mapping of one Reference (s, s2) -> s == null ? s2 : s + "." + s2) //concatenate two strings, prohibit dot at the start of the result ); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/api/RepositoryEntityLinksWithoutProjection.java ================================================ package de.techdev.trackr.core.web.api; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.spi.BackendIdConverter; import org.springframework.data.rest.webmvc.support.PagingAndSortingTemplateVariables; import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; import org.springframework.hateoas.Link; import org.springframework.plugin.core.PluginRegistry; import org.springframework.util.Assert; import java.io.Serializable; /** * Overrides the {@link #linkToSingleResource(Class, Object)} method to not include {?projection}. * @author Moritz Schulze * @deprecated We really should make the frontend not rely on this and just remove it all together. For the migration to * Spring Boot 1.3.3 it's left in so the REST API is as before as much as possible. */ @Deprecated public class RepositoryEntityLinksWithoutProjection extends RepositoryEntityLinks { private final ResourceMappings mappings; private final PluginRegistry> idConverters; /** * Creates a new {@link org.springframework.data.rest.webmvc.support.RepositoryEntityLinks}. * * @param repositories must not be {@literal null}. * @param mappings must not be {@literal null}. * @param config must not be {@literal null}. * @param variables must not be {@literal null}. * @param idConverters must not be {@literal null}. */ public RepositoryEntityLinksWithoutProjection(Repositories repositories, ResourceMappings mappings, RepositoryRestConfiguration config, PagingAndSortingTemplateVariables variables, PluginRegistry> idConverters) { super(repositories, mappings, config, variables, idConverters); this.mappings = mappings; this.idConverters = idConverters; } @Override public Link linkToSingleResource(Class type, Object id) { Assert.isInstanceOf(Serializable.class, id, "Id must be assignable to Serializable!"); ResourceMetadata metadata = mappings.getMetadataFor(type); String mappedId = idConverters.getPluginFor(type, BackendIdConverter.DefaultIdConverter.INSTANCE).toRequestId((Serializable) id, type); return linkFor(type).slash(mappedId).withRel(metadata.getItemResourceRel()); } } ================================================ FILE: src/main/java/de/techdev/trackr/core/web/converters/DateConverter.java ================================================ package de.techdev.trackr.core.web.converters; import org.springframework.core.convert.converter.Converter; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * A {@link java.util.Date} converter that supports the formats yyyy-MM-dd and yyyy-MM-dd HH:mm:ss. * * @author Moritz Schulze */ public class DateConverter implements Converter { private DateFormat date10; private DateFormat date19; public DateConverter() { date10 = new SimpleDateFormat("yyyy-MM-dd"); date19 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } @Override public Date convert(String source) { if(source == null) { return null; } Date date; try { Long milliSeconds = Long.valueOf(source); return new Date(milliSeconds); } catch (NumberFormatException e) { //Continue with other formats } if(source.length() == 10) { try { date = date10.parse(source); } catch (ParseException e) { throw new IllegalArgumentException(String.format("%s is not a valid yyyy-MM-dd date.", source), e); } } else if(source.length() == 19) { try { date = date19.parse(source); } catch (ParseException e) { throw new IllegalArgumentException(String.format("%s is not a valid yyyy-MM-dd HH:mm:ss date.", source), e); } } else { throw new IllegalArgumentException(String.format("%s is not convertible by this Date converter", source)); } return date; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/ApiBeansConfiguration.java ================================================ package de.techdev.trackr.domain; import de.techdev.trackr.core.pdf.PdfRenderer; import de.techdev.trackr.domain.common.UuidMapper; import de.techdev.trackr.domain.employee.expenses.reports.ReportNotifyService; import de.techdev.trackr.domain.employee.expenses.reports.ReportService; import de.techdev.trackr.domain.employee.login.support.SupervisorService; import de.techdev.trackr.domain.employee.sickdays.SickDaysNotifyService; import de.techdev.trackr.domain.employee.vacation.HolidayCalculator; import de.techdev.trackr.domain.employee.vacation.VacationRequestApproveService; import de.techdev.trackr.domain.employee.vacation.support.VacationRequestEmployeeToDaysTotalService; import de.techdev.trackr.domain.employee.vacation.support.VacationRequestNotifyService; import de.techdev.trackr.domain.employee.worktimetracking.WorkTimeTrackingReminderService; import de.techdev.trackr.domain.project.invoice.ChangeStateService; import de.techdev.trackr.domain.project.invoice.InvoiceOverdueService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; /** * All Beans needed for the API that have nothing to do with web. */ @Configuration @ComponentScan(includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = RepositoryEventHandler.class) }) public class ApiBeansConfiguration { @Bean public ReportService travelExpenseReportService() { return new ReportService(); } @Bean public ReportNotifyService travelExpenseReportNotifyService() { return new ReportNotifyService(); } @Bean public WorkTimeTrackingReminderService workTimeTrackingReminderService() { return new WorkTimeTrackingReminderService(); } @Bean public VacationRequestNotifyService vacationRequestNotifyService() { return new VacationRequestNotifyService(); } @Bean public VacationRequestApproveService vacationRequestService() { return new VacationRequestApproveService(); } @Bean public VacationRequestEmployeeToDaysTotalService vacationRequestEmployeeToDaysTotalService() { return new VacationRequestEmployeeToDaysTotalService(); } @Bean public HolidayCalculator holidayCalculator() { return new HolidayCalculator(); } @Bean public InvoiceOverdueService invoiceOverdueService() { return new InvoiceOverdueService(); } @Bean public ChangeStateService changeStateService() { return new ChangeStateService(); } @Bean public SickDaysNotifyService sickDaysNotifyService() { return new SickDaysNotifyService(); } @Bean public SupervisorService supervisorService() { return new SupervisorService(); } @Bean public PdfRenderer pdfRenderer() { return new PdfRenderer(); } @Bean public UuidMapper uuidMapper() { return new UuidMapper(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/common/EmployeeSettingsLocaleResolver.java ================================================ package de.techdev.trackr.domain.common; import de.techdev.trackr.domain.employee.Settings; import de.techdev.trackr.domain.employee.SettingsRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.web.servlet.LocaleContextResolver; import org.springframework.web.servlet.i18n.SessionLocaleResolver; import org.springframework.web.util.WebUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; public class EmployeeSettingsLocaleResolver implements LocaleContextResolver { @Autowired private SettingsRepository settingsRepository; @Override public Locale resolveLocale(HttpServletRequest request) { return getLocaleFromSessionOrAuthentication(request); } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { if(locale != null) { WebUtils.setSessionAttribute(request, SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale); } } @Override public LocaleContext resolveLocaleContext(HttpServletRequest request) { final Locale locale = getLocaleFromSessionOrAuthentication(request); LocaleContext localeContext = () -> locale; LocaleContextHolder.setLocaleContext(localeContext); return localeContext; } @Override public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) { if(localeContext != null) { Locale locale = localeContext.getLocale(); WebUtils.setSessionAttribute(request, SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale); } } /** * Tries to extract the locale from the session. If not present it extracts the locale from the logged in user. Defaults to english. * @param request The request * @return The extracted locale, default {@link java.util.Locale#ENGLISH}. */ protected Locale getLocaleFromSessionOrAuthentication(HttpServletRequest request) { Locale locale = (Locale) WebUtils.getSessionAttribute(request, SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME); if(locale == null) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if(authentication != null) { Object principal = authentication.getPrincipal(); if (User.class.isAssignableFrom(principal.getClass())) { String username = ((User) principal).getUsername(); Settings localeSetting = settingsRepository.findByTypeAndEmployee_Email(Settings.SettingsType.LOCALE, username); if (localeSetting != null) { locale = Locale.forLanguageTag(localeSetting.getValue()); } } } //Either no authentication or admin user if(locale == null) { locale = Locale.ENGLISH; } WebUtils.setSessionAttribute(request, SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale); } LocaleContextHolder.setLocale(locale); return locale; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/common/FederalState.java ================================================ package de.techdev.trackr.domain.common; import com.fasterxml.jackson.annotation.JsonFormat; /** * @author Moritz Schulze */ @JsonFormat(shape= JsonFormat.Shape.OBJECT) public enum FederalState { BADEN_WUERTTEMBERG("Baden-Württemberg"), BAYERN("Bayern"), BERLIN("Berlin"), BRANDENBURG("Brandenburg"), BREMEN("Bremen"), HAMBURG("Hamburg"), HESSEN("Hessen"), MECKLENBURG_VORPOMMERN("Mecklenburg-Vorpommern"), NIEDERSACHSEN("Niedersachsen"), NORDRHEIN_WESTFALEN("Nordrhein-Westfalen"), RHEINLAND_PFALZ("Rheinland-Pfalz"), SAARLAND("Saarland"), SACHSEN("Sachsen"), SACHSEN_ANHALT("Sachsen-Anhalt"), SCHLESWIG_HOLSTEIN("Schleswig-Holstein"), THUERINGEN("Thüringen"); private String state; FederalState(String state) { this.state = state; } public String getState() { return state; } public String getName() { return this.toString(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/common/FederalStateController.java ================================================ package de.techdev.trackr.domain.common; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import static java.util.Arrays.asList; /** * @author Moritz Schulze */ @Controller @RequestMapping("/federalStates") public class FederalStateController { @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public List federalStates() { return asList(FederalState.values()); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/common/UuidMapper.java ================================================ package de.techdev.trackr.domain.common; import org.springframework.beans.factory.annotation.Autowired; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Moritz Schulze */ public class UuidMapper { public static final Pattern UUID_PATTERN = Pattern.compile("^.*([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}).*$"); @Autowired private DataSource dataSource; public String extractUUIDFromString(String input) { Matcher matcher = UuidMapper.UUID_PATTERN.matcher(input); if (matcher.matches()) { return matcher.group(1); } else { return null; } } public Long getIdFromUUID(String uuid) { Long id = null; try(Connection connection = dataSource.getConnection(); PreparedStatement getIdStatement = connection.prepareStatement("SELECT id FROM uuid_mapping WHERE uuid = ?")) { getIdStatement.setString(1, uuid); ResultSet resultSet = getIdStatement.executeQuery(); if (resultSet.next()) { id = resultSet.getLong(1); } } catch (SQLException e) { // just return null } return id; } public void deleteUUID(String uuid) { try(Connection connection = dataSource.getConnection(); PreparedStatement deleteStatement = connection.prepareStatement("DELETE FROM uuid_mapping WHERE uuid = ?")) { deleteStatement.setString(1, uuid); deleteStatement.execute(); } catch (SQLException e) { // nothing to do } } public void deleteUUID(Long id) { try(Connection connection = dataSource.getConnection(); PreparedStatement deleteStatement = connection.prepareStatement("DELETE FROM uuid_mapping WHERE id = ?")) { deleteStatement.setLong(1, id); deleteStatement.execute(); } catch (SQLException e) { // nothing to do } } public UUID createUUID(Long id) { UUID uuid = UUID.randomUUID(); try(Connection connection = dataSource.getConnection(); PreparedStatement insertStatement = connection.prepareStatement("INSERT INTO uuid_mapping (id, uuid) VALUES (?, ?)")) { insertStatement.setLong(1, id); insertStatement.setString(2, uuid.toString()); insertStatement.execute(); } catch (SQLException e) { return null; } return uuid; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/Address.java ================================================ package de.techdev.trackr.domain.company; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; @Entity @Getter @Setter public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotEmpty private String street; @NotEmpty private String houseNumber; @NotEmpty private String zipCode; @NotEmpty private String city; @NotEmpty private String country; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/AddressEventHandler.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.rest.core.annotation.HandleBeforeCreate; import org.springframework.data.rest.core.annotation.HandleBeforeDelete; import org.springframework.data.rest.core.annotation.HandleBeforeSave; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; import org.springframework.security.access.prepost.PreAuthorize; /** * This class prevents other users than admins from creating, updating or deleting addresses. *

* As long as only {@link de.techdev.trackr.domain.company.Company} uses the Address class this is fine, if e.g. {@link de.techdev.trackr.domain.employee.Employee} * gets an address too this will have to be thought over. * * @author Moritz Schulze */ @RepositoryEventHandler(Address.class) @SuppressWarnings("unused") public class AddressEventHandler { @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkSaveAuthority(Address address) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkUpdateAuthority(Address address) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkDeleteAuthority(Address address) { } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/AddressRepository.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.rest.core.annotation.RestResource; import java.util.List; /** * @author Moritz Schulze */ public interface AddressRepository extends JpaRepository { @Override @RestResource(exported = false) List

findAll(); @Override @RestResource(exported = false) Page
findAll(Pageable pageable); @Override @RestResource(exported = false) List
findAll(Sort sort); @Override @RestResource(exported = false) void delete(Long aLong); @Override @RestResource(exported = false) void delete(Address entity); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/Company.java ================================================ package de.techdev.trackr.domain.company; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import de.techdev.trackr.domain.project.Project; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; /** * @author Moritz Schulze */ @Getter @Setter @EqualsAndHashCode(exclude = "contactPersons") @Entity @JsonIgnoreProperties({"debitorProjects"}) public class Company { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotNull @DecimalMin("0") @Column(unique = true) private Long companyId; @NotEmpty private String name; /** * Full days that the company has to pay their invoices. */ private Integer timeForPayment; @NotNull @OneToOne(cascade = CascadeType.PERSIST) @JoinColumn(name = "address_id") private Address address; @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "company") private List contactPersons = new ArrayList<>(); @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "company") private List projects = new ArrayList<>(); @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "debitor") private List debitorProjects = new ArrayList<>(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/CompanyEventHandler.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; import java.util.List; /** * @author Moritz Schulze */ @RepositoryEventHandler(Company.class) @SuppressWarnings("unused") public class CompanyEventHandler { @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkSaveAuthority(Company company) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkUpdateAuthority(Company company) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkDeleteAuthority(Company company) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void beforeAddContactPerson(Company company, List contactPersons) { } @HandleBeforeLinkDelete @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void beforeDeleteContactPerson(Company company, Object contactPerson) { } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/CompanyRepository.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import java.util.List; /** * @author Moritz Schulze */ @RepositoryRestResource(path = "/companies") public interface CompanyRepository extends JpaRepository { Company findByCompanyId(@Param("companyId") Long companyId); List findByNameLikeIgnoreCaseOrderByNameAsc(@Param("name") String name); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/CompanyWithAddressAndContactPersonsProjection.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.rest.core.config.Projection; import java.util.List; /** * @author Moritz Schulze */ @Projection(types = Company.class, name = "withAddressAndContactPersons") public interface CompanyWithAddressAndContactPersonsProjection { Long getId(); Integer getVersion(); Long getCompanyId(); String getName(); Integer getTimeForPayment(); Address getAddress(); List getContactPersons(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/ContactPerson.java ================================================ package de.techdev.trackr.domain.company; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.NotNull; /** * @author Moritz Schulze */ @Entity @Getter @Setter public class ContactPerson { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotNull @ManyToOne @JoinColumn(name = "company") private Company company; @Email private String email; @NotEmpty private String firstName; @NotEmpty private String lastName; private String phone; @NotEmpty private String salutation; private String roles; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/ContactPersonEventHandler.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; /** * @author Moritz Schulze */ @RepositoryEventHandler(ContactPerson.class) @SuppressWarnings("unused") public class ContactPersonEventHandler { @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkCreateAuthority(ContactPerson contactPerson) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkUpdateAuthority(ContactPerson contactPerson) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkDeleteAuthority(ContactPerson contactPerson) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkLinkUpdateAuthority(ContactPerson contactPerson, Object links) { // only security check } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void checkLinkDeleteAuthority(ContactPerson contactPerson, Object linkedEntity) { // only security check } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/company/ContactPersonRepository.java ================================================ package de.techdev.trackr.domain.company; import org.springframework.data.repository.CrudRepository; /** * @author Moritz Schulze */ public interface ContactPersonRepository extends CrudRepository { } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/Employee.java ================================================ package de.techdev.trackr.domain.employee; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.company.Address; import de.techdev.trackr.domain.employee.expenses.reports.Report; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import de.techdev.trackr.domain.project.billtimes.BillableTime; import de.techdev.trackr.domain.project.worktimes.WorkTime; import de.techdev.trackr.domain.validation.constraints.EndAfterBegin; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Represents any employee of techdev. */ @Entity @Getter @Setter @JsonIgnoreProperties({"credential", "workTimes", "billableTimes", "vacationRequests", "approvedRequests", "travelExpenseReports"}) @EndAfterBegin(begin = "joinDate", end = "leaveDate") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotEmpty private String firstName; @NotEmpty private String lastName; @Column(unique = true) @Email @NotEmpty private String email; private String phoneNumber; private String title; private BigDecimal salary; private BigDecimal hourlyCostRate; @Temporal(TemporalType.DATE) private Date joinDate; @Temporal(TemporalType.DATE) private Date leaveDate; @Enumerated(EnumType.STRING) @NotNull private FederalState federalState; private Float vacationEntitlement; @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "employee") private List workTimes = new ArrayList<>(); @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "employee") private List billableTimes = new ArrayList<>(); @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "employee") private List vacationRequests; @OneToMany(mappedBy = "approver") private List approvedRequests; @OneToMany(mappedBy = "employee") private List travelExpenseReports; @OneToOne(orphanRemoval = true) private Address address; private boolean deleted; public String fullName() { return getFirstName() + " " + getLastName(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeController.java ================================================ package de.techdev.trackr.domain.employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.core.RepositoryConstraintViolationException; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; /** * A controller for special operations on Employees not exportable by spring-data-rest */ @Controller @RequestMapping(value = "/employees") public class EmployeeController { @Autowired private SelfEmployeeRepository employeeRepository; /** * This method allows an employee to change some values of his entity on his own, namely the one * in SelfEmployee. * * @param employee The employee to edit. * @param selfEmployee The request body, i.e. the data to change * @return The updated data. */ @ResponseBody @RequestMapping(value = "/{employee}/self", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) public SelfEmployee updateSelf(@PathVariable("employee") Long employee, @RequestBody @Valid SelfEmployee selfEmployee, BindingResult bindingResult) { if (bindingResult.hasErrors()) { throw new RepositoryConstraintViolationException(bindingResult); } return SelfEmployee.valueOf(employeeRepository.save(employee, selfEmployee)); } @RequestMapping(value = "/{employee}/self", method = {RequestMethod.GET}) @ResponseBody public SelfEmployee get(@PathVariable("employee") Long employeeId) { return employeeRepository.findOne(employeeId); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeEventHandler.java ================================================ package de.techdev.trackr.domain.employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; @RepositoryEventHandler(Employee.class) @SuppressWarnings("unused") public class EmployeeEventHandler { @Autowired private SettingsRepository settingsRepository; @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkCreateAuthority(Employee employee) { } @HandleAfterCreate public void createInitialSettings(Employee employee) { Settings settings = new Settings(); settings.setEmployee(employee); settings.setType(Settings.SettingsType.LOCALE); settings.setValue("de"); settingsRepository.save(settings); } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkUpdateAuthority(Employee employee) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkDeleteAuthority(Employee employee) { } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void deleteCredentialForbidden(Employee employee, Object credential) { //deny all, cannot be called } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeRepository.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.trackr.domain.common.FederalState; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import java.util.List; public interface EmployeeRepository extends JpaRepository { @Override @PostAuthorize("hasRole('ROLE_SUPERVISOR') or returnObject?.email == principal?.username") Employee findOne(@Param("id") Long id); @Override @PreAuthorize("hasRole('ROLE_SUPERVISOR')") List findAll(); @Override @PreAuthorize("hasRole('ROLE_SUPERVISOR')") List findAll(Sort sort); @Override @PreAuthorize("hasRole('ROLE_SUPERVISOR')") List findAll(Iterable longs); @Override @PreAuthorize("hasRole('ROLE_SUPERVISOR')") Page findAll(Pageable pageable); @RestResource(exported = false) List findByFederalState(FederalState berlin); /** * Not exported find all method to access all employees without the need for SUPERVISOR/ADMIN rights. * * Filters the admin out. Only used for the address book. */ @RestResource(exported = false) @Query("select e from Employee e where e.email <> 'admin@techdev.de' and e.deleted = false") Page findAllForAddressBook(Pageable pageable); @RestResource(exported = false) Employee findByEmail(String email); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeScheduledJob.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.employee.worktimetracking.WorkTimeTrackingReminderService; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @Slf4j @Setter public class EmployeeScheduledJob { @Autowired private WorkTimeTrackingReminderService workTimeTrackingReminderService; /** * Task that gets triggered in {@link de.techdev.trackr.domain.scheduling.ScheduledJobsConfiguration} by a custom trigger. * * @param state The federal state to send out reminders for. * @return A task that reminds employees in the federal state to track their working times. */ public Runnable sendWorkTimeReminderTask(FederalState state) { return () -> workTimeTrackingReminderService.remindEmployeesToTrackWorkTimes(state); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/Projections.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.company.Address; import org.springframework.data.rest.core.config.Projection; import java.math.BigDecimal; import java.util.Date; public class Projections { @Projection(types = Employee.class, name = "withAddress") public interface WithAddressProjection { Long getId(); Integer getVersion(); String getFirstName(); String getLastName(); String getEmail(); String getPhoneNumber(); String getTitle(); BigDecimal getSalary(); BigDecimal getHourlyCostRate(); Date getJoinDate(); Date getLeaveDate(); FederalState getFederalState(); Float getVacationEntitlement(); Address getAddress(); boolean isDeleted(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/SelfEmployee.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.trackr.domain.company.Address; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.validation.Valid; import java.math.BigDecimal; @Getter @Setter public class SelfEmployee { private Long id; private Integer version; @NotEmpty private String firstName; @NotEmpty private String lastName; private String phoneNumber; private String title; private BigDecimal salary; @Valid private Address address; public static SelfEmployee valueOf(Employee employee) { SelfEmployee selfEmployee = new SelfEmployee(); selfEmployee.setId(employee.getId()); selfEmployee.setFirstName(employee.getFirstName()); selfEmployee.setLastName(employee.getLastName()); selfEmployee.setPhoneNumber(employee.getPhoneNumber()); selfEmployee.setAddress(employee.getAddress()); selfEmployee.setVersion(employee.getVersion()); selfEmployee.setSalary(employee.getSalary()); selfEmployee.setTitle(employee.getTitle()); return selfEmployee; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/SelfEmployeeRepository.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.trackr.domain.company.Address; import de.techdev.trackr.domain.company.AddressRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @Repository public class SelfEmployeeRepository { @Autowired private EmployeeRepository employeeRepository; @Autowired private AddressRepository addressRepository; /** * Update all values on an employee that an employee is allowed to update him/herself. * This is the last name, first name, phone number and address. * @param employeeId The id of the employee to update. * @param selfEmployee The new data for the employee. * @return The updated employee */ @Transactional @PostAuthorize("hasRole('ROLE_ADMIN') or returnObject.email == principal?.username") public Employee save(Long employeeId, SelfEmployee selfEmployee) { Employee employee = employeeRepository.findOne(employeeId); employee.setFirstName(selfEmployee.getFirstName()); employee.setLastName(selfEmployee.getLastName()); employee.setPhoneNumber(selfEmployee.getPhoneNumber()); if (selfEmployee.getAddress() != null) { Address address = addressRepository.save(selfEmployee.getAddress()); employee.setAddress(address); } return employeeRepository.save(employee); } public SelfEmployee findOne(Long employeeId) { return SelfEmployee.valueOf(employeeRepository.findOne(employeeId)); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/Settings.java ================================================ package de.techdev.trackr.domain.employee; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.NotNull; @Entity @Getter @Setter @Table( uniqueConstraints = { @UniqueConstraint(columnNames = {"type", "employee_id"}) } ) public class Settings { public enum SettingsType { LOCALE } @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Enumerated(EnumType.STRING) private SettingsType type; @NotEmpty private String value; @ManyToOne @NotNull @JoinColumn(name = "employee_id") private Employee employee; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/SettingsRepository.java ================================================ package de.techdev.trackr.domain.employee; import org.springframework.data.repository.Repository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import java.util.List; @RepositoryRestResource(exported = false) public interface SettingsRepository extends Repository { Settings save(Settings settings); List findByEmployee_Email(String email); Settings findByTypeAndEmployee_Email(Settings.SettingsType type, String email); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/addressbook/AddressBookController.java ================================================ package de.techdev.trackr.domain.employee.addressbook; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.hateoas.PagedResources; import org.springframework.hateoas.Resources; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.stream.Collectors; /** * @author Moritz Schulze */ @Controller @RequestMapping("/address_book") public class AddressBookController { @Autowired private EmployeeRepository employeeRepository; @PreAuthorize("hasRole('ROLE_EMPLOYEE')") @ResponseBody @RequestMapping(method = RequestMethod.GET, produces = "application/hal+json") public Resources getAddressList( @PageableDefault(sort = "lastName") Pageable pageable ) { Page pageOfEmployees = employeeRepository.findAllForAddressBook(pageable); List employeeForAddressBookDTOs = transformToReducedEmployees(pageOfEmployees.getContent()); return new PagedResources<>(employeeForAddressBookDTOs, pageMetadataFromPage(pageOfEmployees)); } /** * Transform a list of employees to a list of reduced employees * * @param listOfEmployees The list to transform * @return The transformed list. */ protected List transformToReducedEmployees(List listOfEmployees) { return listOfEmployees .stream() .map(EmployeeForAddressBookDTO::valueOf) .collect(Collectors.toList()); } protected PagedResources.PageMetadata pageMetadataFromPage(Page page) { return new PagedResources.PageMetadata(page.getSize(), page.getNumber(), page.getTotalElements(), page.getTotalPages()); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/addressbook/EmployeeForAddressBookDTO.java ================================================ package de.techdev.trackr.domain.employee.addressbook; import de.techdev.trackr.domain.company.Address; import de.techdev.trackr.domain.employee.Employee; import lombok.Getter; import lombok.Setter; /** * Employee only containing the information needed for the address book. */ @Getter @Setter class EmployeeForAddressBookDTO { private Long id; private String firstName; private String lastName; private String phoneNumber; private String title; private String email; private Address address; static EmployeeForAddressBookDTO valueOf(Employee employee) { EmployeeForAddressBookDTO employeeForAddressBookDTO = new EmployeeForAddressBookDTO(); employeeForAddressBookDTO.setId(employee.getId()); employeeForAddressBookDTO.setFirstName(employee.getFirstName()); employeeForAddressBookDTO.setLastName(employee.getLastName()); employeeForAddressBookDTO.setPhoneNumber(employee.getPhoneNumber()); employeeForAddressBookDTO.setTitle(employee.getTitle()); employeeForAddressBookDTO.setEmail(employee.getEmail()); employeeForAddressBookDTO.setAddress(employee.getAddress()); return employeeForAddressBookDTO; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpense.java ================================================ package de.techdev.trackr.domain.employee.expenses; import de.techdev.trackr.domain.employee.expenses.reports.Report; import de.techdev.trackr.domain.validation.constraints.EndAfterBegin; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.Date; @Getter @Setter @Entity @EndAfterBegin(begin = "fromDate", end = "toDate") public class TravelExpense { public enum Type { HOTEL, TAXI, FLIGHT, TRAIN, PARKING, OEPNV, MILEAGE_ALLOWANCE, OTHER } @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotNull @ManyToOne private Report report; @NotNull @Enumerated(EnumType.STRING) private Type type; @NotNull private BigDecimal cost; @NotNull private BigDecimal vat; @NotNull @Temporal(TemporalType.DATE) private Date fromDate; @NotNull @Temporal(TemporalType.DATE) private Date toDate; @NotNull @Temporal(TemporalType.TIMESTAMP) private Date submissionDate; private String comment; private boolean paid; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseEventHandler.java ================================================ package de.techdev.trackr.domain.employee.expenses; import de.techdev.trackr.domain.employee.expenses.reports.Report; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; @RepositoryEventHandler(TravelExpense.class) @SuppressWarnings("unused") public class TravelExpenseEventHandler { @HandleBeforeCreate @PreAuthorize("@travelExpenseEventHandler.canCreate(principal?.username, #travelExpense)") public void checkCreateAuthority(TravelExpense travelExpense) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_SUPERVISOR') or @travelExpenseEventHandler.canEdit(principal?.username, #travelExpense)") public void checkUpdateAuthority(TravelExpense travelExpense) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_SUPERVISOR') or @travelExpenseEventHandler.canDelete(principal?.username, #travelExpense)") public void checkDeleteAuthority(TravelExpense travelExpense) { } @HandleBeforeLinkSave @PreAuthorize("denyAll()") public void denyLinkSave(TravelExpense travelExpense, Object links) { //links are not settable } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void denyLinkDelete(TravelExpense travelExpense, Object linkedEntity) { //links are not deletable. } public boolean canCreate(String email, TravelExpense travelExpense) { return email != null && email.equals(travelExpense.getReport().getEmployee().getEmail()) && travelExpense.getReport().getStatus() != Report.Status.APPROVED && travelExpense.getReport().getStatus() != Report.Status.SUBMITTED; } public boolean canEdit(String email, TravelExpense travelExpense) { return email != null && email.equals(travelExpense.getReport().getEmployee().getEmail()) && travelExpense.getReport().getStatus() != Report.Status.APPROVED && travelExpense.getReport().getStatus() != Report.Status.SUBMITTED; } public boolean canDelete(String email, TravelExpense travelExpense) { return email != null && email.equals(travelExpense.getReport().getEmployee().getEmail()) && travelExpense.getReport().getStatus() != Report.Status.APPROVED && travelExpense.getReport().getStatus() != Report.Status.SUBMITTED; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseRepository.java ================================================ package de.techdev.trackr.domain.employee.expenses; import org.springframework.data.repository.CrudRepository; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PostAuthorize; /** * @author Moritz Schulze */ public interface TravelExpenseRepository extends CrudRepository { @Override @RestResource(exported = false) Iterable findAll(); @Override @PostAuthorize("returnObject?.report.employee.email == principal?.username") TravelExpense findOne(Long aLong); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseTypeController.java ================================================ package de.techdev.trackr.domain.employee.expenses; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import static java.util.Arrays.asList; /** * @author Moritz Schulze */ @Controller @RequestMapping("/travelExpenses") public class TravelExpenseTypeController { @ResponseBody @RequestMapping(value = "types", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public List types() { return asList(TravelExpense.Type.values()); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/Projections.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.expenses.TravelExpense; import de.techdev.trackr.domain.project.Project; import org.springframework.data.rest.core.config.Projection; import java.util.Date; import java.util.List; /** * @author Moritz Schulze */ public class Projections { @Projection(types = Report.class, name = "overview") public interface TravelExpenseReportForOverviewProjection { Long getId(); Employee getEmployee(); Report.Status getStatus(); List getExpenses(); Date getSubmissionDate(); Date getApprovalDate(); Employee getApprover(); Company getDebitor(); Project getProject(); } @Projection(types = Report.class, name = "withEmployeeAndExpenses") public interface TravelExpenseReportWithEmployeeAndTravelExpensesProjection { Long getId(); Integer getVersion(); Employee getEmployee(); List getExpenses(); Report.Status getStatus(); Date getSubmissionDate(); } @Projection(types = Report.class, name = "withExpensesAndDebitor") public interface TravelExpenseReportWithExpensesAndDebitorProjection { Long getId(); Integer getVersion(); List getExpenses(); Report.Status getStatus(); Date getSubmissionDate(); Date getApprovalDate(); Company getDebitor(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/Report.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.expenses.TravelExpense; import de.techdev.trackr.domain.employee.expenses.reports.comments.Comment; import de.techdev.trackr.domain.project.Project; import de.techdev.trackr.domain.validation.constraints.ProjectBelongsToCompany; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author Moritz Schulze */ @Getter @Setter @Entity @Table(name = "travelExpenseReport") @ProjectBelongsToCompany(companyField = "debitor") public class Report { public enum Status { SUBMITTED, PENDING, APPROVED, REJECTED } @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @ManyToOne private Employee employee; @OneToMany(mappedBy = "report", cascade = CascadeType.ALL) private List expenses = new ArrayList<>(); @Enumerated(EnumType.STRING) private Status status; @Temporal(TemporalType.TIMESTAMP) private Date submissionDate; @Temporal(TemporalType.TIMESTAMP) private Date approvalDate; @ManyToOne private Employee approver; @OneToMany(mappedBy = "travelExpenseReport", cascade = CascadeType.REMOVE) private List comments; @ManyToOne(optional = false) private Company debitor; @ManyToOne private Project project; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportController.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.core.pdf.PdfCreationException; import de.techdev.trackr.core.pdf.PdfRenderer; import de.techdev.trackr.domain.employee.expenses.TravelExpense; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.thymeleaf.context.Context; import java.math.BigDecimal; import java.security.Principal; import java.util.Date; import java.util.List; import java.util.Optional; @Controller @RequestMapping("/travelExpenseReports") public class ReportController { @Autowired private ReportService travelExpenseReportService; @Autowired private PdfRenderer pdfRenderer; @Autowired private ReportRepository travelExpenseReportRepository; @RequestMapping(value = "/{id}/submit", method = RequestMethod.PUT) @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public void submit(@PathVariable("id") Report travelExpenseReport) { travelExpenseReportService.submit(travelExpenseReport); } @RequestMapping(value = "/{id}/approve", method = RequestMethod.PUT) @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public void approve(@PathVariable("id") Report travelExpenseReport, Principal principal) { travelExpenseReportService.accept(travelExpenseReport, principal.getName()); } @RequestMapping(value = "/{id}/reject", method = RequestMethod.PUT) @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public void reject(@PathVariable("id") Report travelExpenseReport, Principal principal) { travelExpenseReportService.reject(travelExpenseReport, principal.getName()); } @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #travelExpenseReport.employee.email == principal?.username") @RequestMapping(value = "/{id}/pdf", produces = "application/pdf") @Transactional public ResponseEntity asPdf(@PathVariable("id") Report travelExpenseReport) { Report report = travelExpenseReportRepository.findOne(travelExpenseReport.getId()); Context ctx = new Context(); ctx.setVariable("report", report); ctx.setVariable("today", new Date()); List expenses = report.getExpenses(); BigDecimal totalCost = expenses.stream().map(TravelExpense::getCost).reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal totalReimbursement = expenses.stream() .filter(te -> !te.isPaid()).map(TravelExpense::getCost).reduce(BigDecimal.ZERO, BigDecimal::add); ctx.setVariable("totalCost", totalCost); ctx.setVariable("totalReimbursement", totalReimbursement); Optional startDate = expenses.stream().map(TravelExpense::getFromDate).min(Date::compareTo); Optional endDate = expenses.stream().map(TravelExpense::getToDate).max(Date::compareTo); if (startDate.isPresent()) { ctx.setVariable("startDate", startDate.get()); } if(endDate.isPresent()) { ctx.setVariable("endDate", endDate.get()); } try { byte[] pdf = pdfRenderer.renderPdf("travel-expenses/report", ctx); byte[] pdfBase64 = Base64.encodeBase64(pdf); return new ResponseEntity<>(pdfBase64, HttpStatus.OK); } catch (PdfCreationException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportEventHandler.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; @RepositoryEventHandler(Report.class) @SuppressWarnings("unused") public class ReportEventHandler { @HandleBeforeCreate @PreAuthorize("#travelExpenseReport.employee.email == principal?.username") public void checkCreateAuthority(Report travelExpenseReport) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkUpdateAuthority(Report travelExpenseReport) { } @HandleBeforeDelete @PreAuthorize("@reportEventHandler.employeeCanDeleteReport(#travelExpenseReport, principal?.username) or hasRole('ROLE_ADMIN')") public void checkDeleteAuthority(Report travelExpenseReport) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #travelExpenseReport.employee.email == principal?.username") public void checkLinkSaveAuthority(Report travelExpenseReport, Object links) { //TODO: links is the _old_ content of the link //TODO: how to check for security? the employee should not be able to edit debitor/project but how do we check that? //TODO: it is not possible to prohibit employees from editing links in general because it is used to add travel expenses. if (links != null && Employee.class.isAssignableFrom(links.getClass())) { throw new AccessDeniedException("Employee is not changeable on a travel expense report."); } } @HandleBeforeLinkDelete @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #travelExpenseReport.employee.email == principal?.username") public void checkLinkDeleteAuthority(Report travelExpenseReport, Object linkedEntity) { if(travelExpenseReport.getEmployee() == null) { throw new AccessDeniedException("Employee is not deletable on a travel expense report."); } } public boolean employeeCanDeleteReport(Report report, String username) { return username != null && report.getEmployee().getEmail().equals(username) && (report.getStatus() == Report.Status.PENDING || report.getStatus() == Report.Status.REJECTED); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportNotifyService.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.core.mail.MailService; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.expenses.TravelExpense; import de.techdev.trackr.domain.employee.login.support.SupervisorService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.List; public class ReportNotifyService { @Autowired private MailService mailService; @Autowired private SupervisorService supervisorService; @Autowired private ReportRepository travelExpenseReportRepository; @Value("${trackr.frontendUrl}") private String frontendUrl; /** * Sends a mail to all supervisors except the owning for a submitted travel expense report. * * @param report The report for which the mail is to be sent. */ public void notifySupervisorsOnSubmission(Report report) { String[] emails = supervisorService.getSupervisorEmailsArrayWithout(email -> !report.getEmployee().getEmail().equals(email) ); SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setFrom("no-reply@techdev.de"); mailMessage.setTo(emails); mailMessage.setSubject("New travel expense report by " + fullName(report.getEmployee())); mailMessage.setText( String.format("%s submitted a new travel expense report (#%d) totalling %.2f.\n\nGo to %s to approve or reject it.", fullName(report.getEmployee()), report.getId(), getTotalAmount(report), getWebLink(report)) ); mailService.sendMail(mailMessage); } protected String getWebLink(Report report) { return frontendUrl + "/supervisor/expenses/" + report.getId(); } public void notifyEmployeeOnApproval(Report report) { notifyEmployeeOnStatusChange(report, "approved"); } public void notifyEmployeeOnRejection(Report report) { notifyEmployeeOnStatusChange(report, "rejected"); } private void notifyEmployeeOnStatusChange(Report report, String outcome) { SimpleMailMessage mail = new SimpleMailMessage(); mail.setFrom("no-reply@techdev.de"); mail.setTo(report.getEmployee().getEmail()); mail.setSubject(String.format("Your travel expense report has been %s", outcome)); mail.setText( String.format("%s has %s your travel expense report #%d.", fullName(report.getApprover()), outcome, report.getId()) ); mailService.sendMail(mail); } @Transactional protected BigDecimal getTotalAmount(Report report) { // Since the report passed to this method is not attached to the JPA session anymore we have to reload it to get // the expenses which are lazily fetched. List expenses = travelExpenseReportRepository.findOne(report.getId()).getExpenses(); return expenses.stream() .map(TravelExpense::getCost) .reduce(BigDecimal.ZERO, BigDecimal::add); } protected String fullName(Employee employee) { return employee.getFirstName() + " " + employee.getLastName(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportRepository.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; import java.util.List; @RepositoryRestResource(path = "travelExpenseReports") public interface ReportRepository extends CrudRepository { @Override @RestResource(exported = false) Iterable findAll(); @Override @PostAuthorize("hasRole('ROLE_SUPERVISOR') or returnObject?.employee?.email == principal?.username") Report findOne(Long aLong); @PreAuthorize("#employee.email == principal?.username") Page findByEmployeeAndStatusOrderByStatusAsc(@Param("employee") Employee employee, @Param("status") Report.Status status, Pageable pageable); @PreAuthorize("hasRole('ROLE_SUPERVISOR')") Page findByStatus(@Param("status") Report.Status status, Pageable pageable); @PreAuthorize("hasRole('ROLE_ADMIN')") List findBySubmissionDateBetween(@Param("start") Date start, @Param("end") Date end); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportService.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import java.util.Date; @Transactional public class ReportService { @Autowired private ReportRepository travelExpenseReportRepository; @Autowired private EmployeeRepository employeeRepository; @Autowired private ReportNotifyService travelExpenseReportNotifyService; @PreAuthorize("#travelExpenseReport.employee.email == principal?.username") public Report submit(Report travelExpenseReport) { travelExpenseReportNotifyService.notifySupervisorsOnSubmission(travelExpenseReport); return setStatusOnTravelExpenseReport(travelExpenseReport, Report.Status.SUBMITTED, null); } @PreAuthorize("hasRole('ROLE_SUPERVISOR') and #travelExpenseReport.employee.email != principal?.username") public Report accept(Report travelExpenseReport, String approverName) { Report acceptedReport = setStatusOnTravelExpenseReport(travelExpenseReport, Report.Status.APPROVED, approverName); travelExpenseReportNotifyService.notifyEmployeeOnApproval(acceptedReport); return acceptedReport; } @PreAuthorize("hasRole('ROLE_SUPERVISOR') and #travelExpenseReport.employee.email != principal?.username") public Report reject(Report travelExpenseReport, String rejecterName) { Report rejectedReport = setStatusOnTravelExpenseReport(travelExpenseReport, Report.Status.REJECTED, rejecterName); travelExpenseReportNotifyService.notifyEmployeeOnRejection(rejectedReport); return rejectedReport; } private Report setStatusOnTravelExpenseReport(Report travelExpenseReport, Report.Status status, String approverName) { if (status == Report.Status.SUBMITTED) { travelExpenseReport.setSubmissionDate(new Date()); } else { Employee approver = employeeRepository.findByEmail(approverName); travelExpenseReport.setApprover(approver); travelExpenseReport.setApprovalDate(new Date()); } travelExpenseReport.setStatus(status); return travelExpenseReportRepository.save(travelExpenseReport); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/Comment.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports.comments; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.expenses.reports.Report; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.util.Date; /** * @author Moritz Schulze */ @Entity @Getter @Setter @Table(name = "travelExpenseReportComment") public class Comment { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotEmpty private String text; @NotNull @Temporal(TemporalType.TIMESTAMP) private Date submissionDate; @ManyToOne @NotNull private Report travelExpenseReport; @ManyToOne @NotNull private Employee employee; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentEventHandler.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports.comments; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; @RepositoryEventHandler(value = Comment.class) @SuppressWarnings("unused") public class CommentEventHandler { @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #comment.employee.email == principal?.username") public void checkCreateAuthority(Comment comment) { comment.setSubmissionDate(new Date()); } @HandleBeforeSave @PreAuthorize("denyAll()") public void checkUpdateAuthority(Comment comment) { //deny all } @HandleBeforeLinkSave @PreAuthorize("denyAll()") public void checkLinkSaveAuthority(Comment comment, Object links) { //deny all } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void checkLinkDeleteAuthority(Comment comment, Object linkedEntity) { //deny all } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentRepository.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports.comments; import de.techdev.trackr.domain.employee.expenses.reports.Report; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PreAuthorize; import java.util.List; @RepositoryRestResource(path = "travelExpenseReportComments") public interface CommentRepository extends CrudRepository { @Override @RestResource(exported = false) Iterable findAll(); @Override @RestResource(exported = false) Comment findOne(Long aLong); @Override @RestResource(exported = false) void delete(Long aLong); @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #travelExpenseReport.employee.email == principal?.username") List findByTravelExpenseReportOrderBySubmissionDateAsc(@Param("report") Report travelExpenseReport); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentWithEmployeeProjection.java ================================================ package de.techdev.trackr.domain.employee.expenses.reports.comments; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.rest.core.config.Projection; import java.util.Date; /** * @author Moritz Schulze */ @Projection(types = Comment.class, name = "withEmployee") public interface CommentWithEmployeeProjection { Long getId(); String getText(); Date getSubmissionDate(); Employee getEmployee(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/login/PrincipalController.java ================================================ package de.techdev.trackr.domain.employee.login; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import de.techdev.trackr.domain.employee.Settings; import de.techdev.trackr.domain.employee.SettingsRepository; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.security.Principal; import java.util.Collection; @Controller @Slf4j public class PrincipalController { @Getter @Setter static class ReturnValue { Collection authorities; String locale; Long id; String email; } @Autowired private EmployeeRepository employeeRepository; @Autowired private SettingsRepository settingsRepository; @RequestMapping(value = "/principal", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ReturnValue principal(Principal principal) { if (principal != null) { Employee employee = employeeRepository.findByEmail(principal.getName()); if (employee != null) { Settings localeSettings = settingsRepository.findByTypeAndEmployee_Email(Settings.SettingsType.LOCALE, employee.getEmail()); ReturnValue value = new ReturnValue(); value.locale = localeSettings.getValue(); value.id = employee.getId(); value.authorities = ((Authentication) principal).getAuthorities(); value.email = principal.getName(); return value; } else { log.error("Somehow someone with an invalid email is in our system: {}", principal.getName()); throw new IllegalStateException("Invalid email address in principal."); } } else { return null; } } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/login/support/SupervisorService.java ================================================ package de.techdev.trackr.domain.employee.login.support; import de.techdev.trackr.core.security.AuthorityService; import org.springframework.beans.factory.annotation.Autowired; import java.util.Collection; import java.util.function.Predicate; public class SupervisorService { @Autowired private AuthorityService authorityService; /** * @return Email addresses of all supervisors */ public String[] getSupervisorEmailsAsArray() { return getSupervisorEmailsArrayWithout(c -> true); } /** * @param withoutThese A predicate to filter out supervisors, e.g. the logged in principal. * @return Email addresses of all supervisors for whose credentials the predicate returns true. */ public String[] getSupervisorEmailsArrayWithout(Predicate withoutThese) { Collection supervisor = authorityService.getEmployeeEmailsByAuthority("ROLE_SUPERVISOR"); return supervisor.stream().filter(withoutThese).toArray(String[]::new); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDays.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.validation.constraints.EndAfterBegin; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.util.Date; /** * @author Moritz Schulze */ @Entity @Getter @Setter @EndAfterBegin(begin = "startDate", end = "endDate") public class SickDays { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @ManyToOne @NotNull private Employee employee; @Temporal(TemporalType.DATE) @NotNull private Date startDate; @Temporal(TemporalType.DATE) private Date endDate; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysEventHandler.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; @RepositoryEventHandler(value = SickDays.class) @SuppressWarnings("unused") public class SickDaysEventHandler { @Autowired private SickDaysNotifyService sickDaysNotifyService; @HandleBeforeCreate @PreAuthorize("#sickDays.employee.email == principal?.username or hasRole('ROLE_ADMIN')") public void checkSaveAuthority(SickDays sickDays) { sickDaysNotifyService.notifySupervisorsAboutNew(sickDays); } @HandleBeforeSave @PreAuthorize("#sickDays.employee.email == principal?.username or hasRole('ROLE_ADMIN')") public void checkUpdateAuthority(SickDays sickDays) { sickDaysNotifyService.notifySupervisorsAboutUpdate(sickDays); } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkDeleteAuthority(SickDays sickDays) { } @HandleBeforeLinkSave @PreAuthorize("denyAll()") public void checkLinkUpdateAuthority(SickDays sickDays, Object links) { //deny all } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void checkLinkSaveAuthority(SickDays sickDays, Object linkedEntity) { //deny all } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysNotifyService.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import de.techdev.trackr.core.mail.MailService; import de.techdev.trackr.domain.employee.login.support.SupervisorService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.SimpleMailMessage; import java.text.SimpleDateFormat; /** * @author Moritz Schulze */ public class SickDaysNotifyService { @Autowired private MailService mailService; @Autowired private SupervisorService supervisorService; public void notifySupervisorsAboutNew(SickDays sickDays) { SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); String subject = "Sick days reported by " + sickDays.getEmployee().fullName(); String text = sickDays.getEmployee().fullName() + " reported sick days from " + sdf.format(sickDays.getStartDate()) + " to " + (sickDays.getEndDate() != null ? sdf.format(sickDays.getEndDate()) : " (not set)" ) + "."; notifySupervisors(subject, text); } public void notifySupervisorsAboutUpdate(SickDays sickDays) { SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); String subject = "Sick days updated by " + sickDays.getEmployee().fullName(); String text = sickDays.getEmployee().fullName() + " updated his sick days. Interval is now from " + sdf.format(sickDays.getStartDate()) + " to " + (sickDays.getEndDate() != null ? sdf.format(sickDays.getEndDate()) : " (not set)") + "."; notifySupervisors(subject, text); } protected void notifySupervisors(String subject, String text) { String[] receiver = supervisorService.getSupervisorEmailsAsArray(); SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setSubject(subject); mailMessage.setTo(receiver); mailMessage.setText(text); mailMessage.setFrom("no-reply@techdev.de"); mailService.sendMail(mailMessage); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysRepository.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; import java.util.List; @RepositoryRestResource(path = "sickDays") public interface SickDaysRepository extends JpaRepository { @Override @RestResource(exported = false) Page findAll(Pageable pageable); @Override @PostAuthorize("hasRole('ROLE_ADMIN') or principal?.username == returnObject.employee.email") SickDays findOne(Long aLong); @PreAuthorize("#employee.email == principal?.username") List findByEmployee(@Param("employee") Employee employee); @PreAuthorize("hasRole('ROLE_ADMIN')") List findByStartDateBetweenOrEndDateBetween( @Param("startLower") Date startLower, @Param("startHigher") Date startHigher, @Param("endLower") Date endLower, @Param("endHigher") Date endHigher ); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysWithEmployeeProjection.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.rest.core.config.Projection; import java.util.Date; /** * @author Moritz Schulze */ @Projection(types = SickDays.class, name = "withEmployee") public interface SickDaysWithEmployeeProjection { Long getId(); Integer getVersion(); Employee getEmployee(); Date getStartDate(); Date getEndDate(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/Holiday.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.common.FederalState; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import java.util.Date; /** * @author Moritz Schulze */ @Entity @Getter @Setter public class Holiday { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Temporal(TemporalType.DATE) private Date day; private String name; @Enumerated(EnumType.STRING) private FederalState federalState; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/HolidayCalculator.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.util.LocalDateUtil; import org.springframework.beans.factory.annotation.Autowired; import java.time.DayOfWeek; import java.time.LocalDate; import java.util.Date; import java.util.List; import static java.util.stream.Collectors.toList; /** * @author Moritz Schulze */ public class HolidayCalculator { @Autowired private HolidayRepository holidayRepository; public Integer calculateDifferenceBetweenExcludingHolidaysAndWeekends(Date start, Date end, FederalState federalState) { List holidays = holidayRepository.findByFederalStateAndDayBetween(federalState, start, end); return calculateDifferenceBetweenExcludingHolidaysAndWeekends(LocalDateUtil.fromDate(start), LocalDateUtil.fromDate(end), holidays.stream() .map(holiday -> LocalDateUtil.fromDate(holiday.getDay())) .collect(toList())); } protected Integer calculateDifferenceBetweenExcludingHolidaysAndWeekends(LocalDate start, LocalDate end, List holidays) { LocalDate step = start; Integer count = 0; while(step.isBefore(end.plusDays(1))) { if(!isWeekendOrHoliday(step, holidays)) { count++; } step = step.plusDays(1); } return count; } /** * Checks whether a date is a weekend day or in a given list of holidays. * * @param date The date to check * @param holidays A list of dates that are seens as holidays * @return True if the date is a sunday or saturday or in the list. */ protected boolean isWeekendOrHoliday(LocalDate date, List holidays) { return date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY || holidays.stream().anyMatch(date::equals); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/HolidayRepository.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.common.FederalState; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RestResource; import java.util.Date; import java.util.List; /** * @author Moritz Schulze */ public interface HolidayRepository extends CrudRepository { @Override @RestResource(exported = false) S save(S entity); @Override @RestResource(exported = false) void delete(Long id); List findByFederalStateAndDayBetween(@Param("state") FederalState federalState, @Param("start") Date start, @Param("end") Date end); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import com.fasterxml.jackson.annotation.JsonIgnore; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.validation.constraints.EndAfterBegin; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.util.Date; /** * @author Moritz Schulze */ @Entity @Getter @Setter @EndAfterBegin(begin = "startDate", end = "endDate") public class VacationRequest { public enum VacationRequestStatus { APPROVED, PENDING, REJECTED } @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @ManyToOne @NotNull private Employee employee; @Temporal(TemporalType.DATE) @NotNull private Date startDate; @Temporal(TemporalType.DATE) @NotNull private Date endDate; private Integer numberOfDays; @Enumerated(EnumType.STRING) private VacationRequestStatus status; @Temporal(TemporalType.TIMESTAMP) private Date approvalDate; @Temporal(TemporalType.TIMESTAMP) private Date submissionTime; @ManyToOne private Employee approver; @JsonIgnore public boolean isApproved() { return status == VacationRequestStatus.APPROVED; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestApproveService.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.common.UuidMapper; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import de.techdev.trackr.domain.employee.vacation.support.VacationRequestNotifyService; import de.techdev.trackr.util.LocalDateUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.Date; import java.util.List; @Slf4j public class VacationRequestApproveService { @Autowired private VacationRequestRepository vacationRequestRepository; @Autowired private EmployeeRepository employeeRepository; @Autowired private VacationRequestNotifyService vacationRequestNotifyService; @Autowired private UuidMapper uuidMapper; @Transactional @PreAuthorize("hasRole('ROLE_SUPERVISOR') and principal?.username != #vacationRequest.employee.email") public VacationRequest approve(VacationRequest vacationRequest, String supervisorEmail) { return setStatusOnVacationRequest(vacationRequest, supervisorEmail, VacationRequest.VacationRequestStatus.APPROVED); } /** * Rejects a vacation request. The vacation request will be fetched from the repository, the rejector by the name given in * supervisor email. *

* Will only reject pending vacation requests. * * @param vacationRequest The vacation request to reject. * @param supervisorEmail The email address of the employee to use as the approver. * @return The approved (or not) vacation request. */ @Transactional @PreAuthorize("hasRole('ROLE_SUPERVISOR') and principal?.username != #vacationRequest.employee.email") public VacationRequest reject(VacationRequest vacationRequest, String supervisorEmail) { return setStatusOnVacationRequest(vacationRequest, supervisorEmail, VacationRequest.VacationRequestStatus.REJECTED); } /** * Approves all requests that are more than seven days old. */ @Transactional @PreAuthorize("hasRole('ROLE_ADMIN')") public void approveSevenDayOldRequests() { LocalDate oneWeekAgo = LocalDate.now().minusDays(7); List vacationRequests = vacationRequestRepository.findBySubmissionTimeBeforeAndStatus(LocalDateUtil.fromLocalDate(oneWeekAgo), VacationRequest.VacationRequestStatus.PENDING); vacationRequests.forEach(vacationRequest -> { log.info("Approving more then seven days old vacation request {}", vacationRequest); approve(vacationRequest, null); }); } protected VacationRequest setStatusOnVacationRequest(VacationRequest vacationRequest, String supervisorEmail, VacationRequest.VacationRequestStatus status) { if (vacationRequest.getStatus() == VacationRequest.VacationRequestStatus.PENDING) { Employee supervisor = null; Employee byEmail = employeeRepository.findByEmail(supervisorEmail); if (byEmail != null) { supervisor = byEmail; } vacationRequest.setStatus(status); vacationRequest.setApprover(supervisor); vacationRequest.setApprovalDate(new Date()); vacationRequest = vacationRequestRepository.save(vacationRequest); vacationRequestNotifyService.sendEmailNotification(vacationRequest); } uuidMapper.deleteUUID(vacationRequest.getId()); return vacationRequest; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestController.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.employee.vacation.support.VacationRequestEmployeeToDaysTotalService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.security.Principal; import java.util.Date; import java.util.Map; /** * @author Moritz Schulze */ @Controller @RequestMapping("/vacationRequests") @Slf4j public class VacationRequestController { @Autowired private VacationRequestApproveService vacationRequestApproveService; @Autowired private VacationRequestEmployeeToDaysTotalService vacationRequestEmployeeToDaysTotalService; /** * Approve a vacation request. Can only be done by supervisors, but they are not allowed to approve their own requests. * Only works on pending requests. * * @param vacationRequest The vacation request to approve * @param principal The Spring Security principal to extract the user from. * @return The vacation request object. */ @PreAuthorize("hasRole('ROLE_SUPERVISOR')") @RequestMapping(value = "/{id}/approve", method = RequestMethod.PUT) @ResponseStatus(HttpStatus.OK) @ResponseBody public VacationRequest approve(@PathVariable("id") VacationRequest vacationRequest, Principal principal) { return vacationRequestApproveService.approve(vacationRequest, principal.getName()); } /** * Reject a vacation request. Can only be done by supervisors, but they are not allowed to reject their own requests. * Only works on pending requests. * * @param vacationRequest The vacation request to reject. * @param principal The Spring Security principal to extract the user from. * @return The vacation request object. */ @PreAuthorize("hasRole('ROLE_SUPERVISOR')") @RequestMapping(value = "/{id}/reject", method = RequestMethod.PUT) @ResponseStatus(HttpStatus.OK) @ResponseBody public VacationRequest reject(@PathVariable("id") VacationRequest vacationRequest, Principal principal) { return vacationRequestApproveService.reject(vacationRequest, principal.getName()); } /** * Create a mapping of employee names to the effective number of vacation days between a start and end date. * * All approved vacation requests that coincide with the period are collected. Only the cut of every vacation request with the period is used for the calculation. * Public holidays and weekends are excluded. */ @PreAuthorize("hasRole('ROLE_ADMIN')") @ResponseBody @RequestMapping(value = "/daysPerEmployeeBetween", method = RequestMethod.GET, produces = "application/hal+json") @ResponseStatus(HttpStatus.OK) public Map daysPerEmployeeBetween( @RequestParam("start") Date start, @RequestParam("end") Date end ) { return vacationRequestEmployeeToDaysTotalService.mapVacationRequestsToTotalDays(start, end); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestEventHandler.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.common.UuidMapper; import de.techdev.trackr.domain.employee.vacation.support.VacationRequestNotifyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; import java.util.UUID; @RepositoryEventHandler(VacationRequest.class) @SuppressWarnings("unused") public class VacationRequestEventHandler { @Autowired private HolidayCalculator holidayCalculator; @Autowired private VacationRequestNotifyService vacationRequestNotifyService; @Autowired private UuidMapper uuidMapper; @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_ADMIN') or principal?.username == #vacationRequest.employee.email") public void prepareVacationRequest(VacationRequest vacationRequest) { Integer difference = holidayCalculator.calculateDifferenceBetweenExcludingHolidaysAndWeekends(vacationRequest.getStartDate(), vacationRequest.getEndDate(), vacationRequest.getEmployee().getFederalState()); vacationRequest.setNumberOfDays(difference); vacationRequest.setStatus(VacationRequest.VacationRequestStatus.PENDING); vacationRequest.setApprover(null); vacationRequest.setApprovalDate(null); vacationRequest.setSubmissionTime(new Date()); } @HandleAfterCreate public void afterCreation(VacationRequest vacationRequest) { UUID uuid = uuidMapper.createUUID(vacationRequest.getId()); vacationRequestNotifyService.notifySupervisors(vacationRequest, uuid); } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_SUPERVISOR') and principal?.username != #vacationRequest.employee.email") public void authorizeUpdate(VacationRequest vacationRequest) { } @HandleBeforeDelete @PreAuthorize("( hasRole('ROLE_SUPERVISOR') and @vacationRequestEventHandler.supervisorCanDeleteRequest(principal?.username, #vacationRequest) ) " + "or @vacationRequestEventHandler.employeeCanDeleteRequest(principal?.username, #vacationRequest)") public void authorizeDelete(VacationRequest vacationRequest) { } @HandleBeforeLinkSave @PreAuthorize("denyAll()") public void denyLinksSave(VacationRequest vacationRequest, Object links) { //deny all, cannot be called } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void denyLinks(VacationRequest vacationRequest, Object linkedEntity) { //deny all, cannot be called } /** * Test if an employee may delete a vacation request. This means it is his own request and it is pending. * @param username The email of the logged in user * @param request The vacation request * @return true if the user may delete, false otherwise. */ public boolean employeeCanDeleteRequest(String username, VacationRequest request) { return username != null && username.equals(request.getEmployee().getEmail()) && request.getStatus() == VacationRequest.VacationRequestStatus.PENDING; } public boolean supervisorCanDeleteRequest(String username, VacationRequest request) { return username != null && !username.equals(request.getEmployee().getEmail()); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestRepository.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; import java.util.List; public interface VacationRequestRepository extends CrudRepository { @Override @PostAuthorize("hasRole('ROLE_SUPERVISOR') or principal?.username == returnObject.employee.email") VacationRequest findOne(Long aLong); @Override @RestResource(exported = false) Iterable findAll(); @PreAuthorize("hasRole('ROLE_SUPERVISOR') or principal?.username == #employee.email") List findByEmployeeOrderByStartDateAsc(@Param("employee") Employee employee); @PreAuthorize("hasRole('ROLE_SUPERVISOR')") List findByStatusOrderBySubmissionTimeAsc(@Param("status") VacationRequest.VacationRequestStatus status); @RestResource(exported = false) List findBySubmissionTimeBeforeAndStatus(Date date, VacationRequest.VacationRequestStatus status); /** * Find vacation requests of a certain status that overlap with a period. */ @RestResource(exported = false) @Query("SELECT vr FROM VacationRequest vr WHERE (vr.startDate BETWEEN :startLower AND :startHigher OR vr.endDate BETWEEN :endLower AND :endHigher) AND vr.status = :status") List findByStartDateBetweenOrEndDateBetweenAndStatus( @Param("startLower") Date startLower, @Param("startHigher") Date startHigher, @Param("endLower") Date endLower, @Param("endHigher") Date endHigher, @Param("status") VacationRequest.VacationRequestStatus status); /** * Find one by id without security. * @param id The id of the vacation request to find. */ @RestResource(exported = false) @Query("SELECT vr FROM VacationRequest vr WHERE vr.id = :id") VacationRequest findOneWithoutSecurity(@Param("id") Long id); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestScheduledJobs.java ================================================ package de.techdev.trackr.domain.employee.vacation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; /** * @author Moritz Schulze */ @Slf4j public class VacationRequestScheduledJobs { @Autowired private VacationRequestApproveService vacationRequestApproveService; @Scheduled(cron = "0 0 4 * * *") public void approveSevenDaysOldVacationRequests() { log.debug("Executing vacation request approval trigger."); vacationRequestApproveService.approveSevenDayOldRequests(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestWithEmployeeAndApproverProjection.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.rest.core.config.Projection; import java.util.Date; /** * @author Moritz Schulze */ @Projection(types = VacationRequest.class, name = "withEmployeeAndApprover") public interface VacationRequestWithEmployeeAndApproverProjection { Long getId(); Employee getEmployee(); Date getStartDate(); Date getEndDate(); Integer getNumberOfDays(); VacationRequest.VacationRequestStatus getStatus(); Date getApprovalDate(); Date getSubmissionTime(); Employee getApprover(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/MailApproveService.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import de.techdev.trackr.domain.common.UuidMapper; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import de.techdev.trackr.domain.employee.vacation.VacationRequestApproveService; import de.techdev.trackr.domain.employee.vacation.VacationRequestRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import javax.mail.Message; import javax.mail.MessagingException; import java.io.IOException; @Slf4j @Service @Profile("gmail") public class MailApproveService { @Autowired private UuidMapper uuidMapper; @Autowired private VacationRequestRepository vacationRequestRepository; @Autowired private VacationRequestApproveService vacationRequestApproveService; public void approveOrRejectFromMail(Message mail) { log.debug("Got mail."); String subject; String content; String from; MessageWrapper message = new MessageWrapper(mail); try { subject = mail.getSubject(); content = message.extractContentAsString(); from = message.getSender(); } catch (MessagingException | IOException e) { throw new RuntimeException("Could not extract uuid or content from the mail.", e); } String uuid = uuidMapper.extractUUIDFromString(subject); if (uuid == null) { throw new RuntimeException("Could find a UUID in the subject " + subject); } log.debug("Extracted UUID {} from subject." , uuid); Long id = uuidMapper.getIdFromUUID(uuid); if (id != null) { log.debug("Got ID {} from the mapper.", id); actualApprove(id, from, content); } else { log.debug("Did not find an ID for UUID {}, deleting record.", uuid); uuidMapper.deleteUUID(uuid); } } /** * Analyze the text of a mail and decide if the vacation request should be approved or rejected. * If the word "approve" appears more often approve, if the word "reject" appears more often reject. If * they appear with the same number, throw an {@link java.lang.IllegalArgumentException}. * @param mailContent The content of the mail. * @return The status APPROVED when to approve and the status REJECTED when to reject. */ protected VacationRequest.VacationRequestStatus approveOrReject(String mailContent) { String mailContentLowerCase = mailContent.toLowerCase(); int approveCount = StringUtils.countOccurrencesOf(mailContentLowerCase, "approve"); int rejectCount = StringUtils.countOccurrencesOf(mailContentLowerCase, "reject"); if (approveCount > rejectCount) { return VacationRequest.VacationRequestStatus.APPROVED; } else if (approveCount < rejectCount) { return VacationRequest.VacationRequestStatus.REJECTED; } else { throw new IllegalStateException("Cannot decide from the text if to approve or reject"); } } protected void actualApprove(Long vacationRequestId, String supervisorEmail, String content) { VacationRequest vacationRequest = vacationRequestRepository.findOneWithoutSecurity(vacationRequestId); if (vacationRequest == null) { log.debug("Did not find a vacation request for id {}.", vacationRequestId); return; } if (vacationRequest.getEmployee().getEmail().equals(supervisorEmail)) { log.warn("Supervisor {} tried to approve his/her own vacation request via mail.", supervisorEmail); return; } VacationRequest.VacationRequestStatus status = approveOrReject(content); if (status == VacationRequest.VacationRequestStatus.APPROVED) { vacationRequestApproveService.approve(vacationRequest, supervisorEmail); } else if (status == VacationRequest.VacationRequestStatus.REJECTED) { vacationRequestApproveService.reject(vacationRequest, supervisorEmail); } } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/MessageWrapper.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMultipart; import java.io.ByteArrayOutputStream; import java.io.IOException; class MessageWrapper { private final Message message; public MessageWrapper(Message message) { this.message = message; } /** * Extract the content of a Mail as a String depending on the content type. * text/plain -> directly * multipart/mime -> only the text/plain part if it exists, null otherwise * multipart -> the body * @return * @throws IOException * @throws MessagingException */ String extractContentAsString() throws IOException, MessagingException { Object content = message.getContent(); if (content instanceof String) { return (String) content; } if(content instanceof MimeMultipart) { MimeMultipart multipart = (MimeMultipart) content; for (int i = 0; i < multipart.getCount(); i++) { if (multipart.getBodyPart(i).getContentType().startsWith("text/plain")) { return (String)multipart.getBodyPart(i).getContent(); } } return null; } if (content instanceof Multipart) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ((Multipart) content).writeTo(outputStream); return new String(outputStream.toByteArray(), "UTF-8"); } throw new IllegalArgumentException("Incompatible message type."); } /** * Extracts the sender of an email if it is an InternetAddress. * @return The address or null if it's not a techdev address. * @throws MessagingException */ String getSender() throws MessagingException { String from = null; Address[] fromArray = message.getFrom(); if (fromArray.length > 0) { Address address = message.getFrom()[0]; if (address instanceof InternetAddress) { from = ((InternetAddress) address).getAddress(); if (!from.endsWith("techdev.de")) { from = null; } } } return from; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestEmployeeToDaysTotalService.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import de.techdev.trackr.domain.employee.vacation.HolidayCalculator; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import de.techdev.trackr.domain.employee.vacation.VacationRequestRepository; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; import java.util.List; import java.util.Map; import java.util.function.ToIntFunction; import java.util.stream.Collectors; /** * @author Moritz Schulze */ public class VacationRequestEmployeeToDaysTotalService { @Autowired private HolidayCalculator holidayCalculator; @Autowired private VacationRequestRepository vacationRequestRepository; /** * Groups vacation requests by their employee (the full name to be precise) and sums up all _actual_ vacation days between start and * end for all vacation requests of that employee. * @param start The start of the period * @param end The end of the period * @return A map of employee names to days of vacation in between start and end. */ public Map mapVacationRequestsToTotalDays(Date start, Date end) { List vacationRequests = vacationRequestRepository .findByStartDateBetweenOrEndDateBetweenAndStatus(start, end, start, end, VacationRequest.VacationRequestStatus.APPROVED); return mapToEmployeesAndSumUp(vacationRequests, vacationRequest -> getVacationDaysBetween(vacationRequest, start, end)); } /** * Groups vacation requests by employee names and adds for every vacation request the numbers returned by the mapper. * @param vacationRequests The vacation requests to group * @param numberExtractor A function that maps a vacation request to a number. * @return A map of the employee names to the sum of numbers returned by the mapper for their vacation requests. */ protected Map mapToEmployeesAndSumUp(List vacationRequests, ToIntFunction numberExtractor) { return vacationRequests.stream().collect( Collectors.groupingBy( vacationRequest -> vacationRequest.getEmployee().getFirstName() + " " + vacationRequest.getEmployee().getLastName(), Collectors.summingInt(numberExtractor) ) ); } /** * @return Returns the number of days of the vacation request between start and end that aren't holidays or weekends. */ protected Integer getVacationDaysBetween(VacationRequest vacationRequest, Date start, Date end) { return holidayCalculator.calculateDifferenceBetweenExcludingHolidaysAndWeekends( // If the start of the vacation request is before the desired period we use the period start getMaximum(start, vacationRequest.getStartDate()), // If the end of the vacation request is after the desired period we use the period end getMinimum(end, vacationRequest.getEndDate()), vacationRequest.getEmployee().getFederalState() ); } /** * The minimum of the two dates. No null checks. */ protected Date getMinimum(Date a, Date b) { return a.before(b) ? a : b; } /** * The maximum of the two dates. No null checks. */ protected Date getMaximum(Date a, Date b) { return a.before(b) ? b : a; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestNotifyService.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import de.techdev.trackr.core.mail.MailService; import de.techdev.trackr.domain.employee.login.support.SupervisorService; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.SimpleMailMessage; import java.text.SimpleDateFormat; import java.util.UUID; public class VacationRequestNotifyService { @Autowired private MailService mailService; @Autowired private SupervisorService supervisorService; /** * Sends an approval or rejection mail depending on the status of the parameter. * @param request The request for which the mail is to sent. */ public void sendEmailNotification(VacationRequest request) { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setFrom("no-reply@techdev.de"); mailMessage.setTo(request.getEmployee().getEmail()); mailMessage.setSubject("Your vacation request has been " + statusPastVerb(request.getStatus())); mailMessage.setText(getStatusMailText(request)); mailService.sendMail(mailMessage); } protected String statusPastVerb(VacationRequest.VacationRequestStatus status) { if(status == VacationRequest.VacationRequestStatus.APPROVED) { return "approved"; } else if (status == VacationRequest.VacationRequestStatus.REJECTED) { return "rejected"; } else { return "is pending"; } } protected String getStatusMailText(VacationRequest request) { if(request.getApprover() == null) { return "Your vacation request has been automatically approved."; } else { return request.getApprover().fullName() + " has " + statusPastVerb(request.getStatus()) + " your vacation request from " + request.getSubmissionTime(); } } /** * Send a new vacation request notification to all supervisors. */ public void notifySupervisors(VacationRequest vacationRequest, UUID uuid) { SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); String[] receiver = supervisorService.getSupervisorEmailsAsArray(); SimpleMailMessage mailMessage = new SimpleMailMessage(); String subject = "New vacation request from " + vacationRequest.getEmployee().fullName() + "; " + uuid.toString(); String text = "New vacation request from " + vacationRequest.getEmployee().fullName() + " for " + sdf.format(vacationRequest.getStartDate()) + " - " + sdf .format(vacationRequest.getEndDate()) + ". You can reply to this email with approve or reject to do that."; mailMessage.setSubject(subject); mailMessage.setTo(receiver); mailMessage.setText(text); mailMessage.setFrom("no-reply@techdev.de"); mailService.sendMail(mailMessage); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/employee/worktimetracking/WorkTimeTrackingReminderService.java ================================================ package de.techdev.trackr.domain.employee.worktimetracking; import de.techdev.trackr.core.mail.MailService; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import java.util.List; /** * Remind employees to track their working times. */ public class WorkTimeTrackingReminderService { @Autowired private EmployeeRepository employeeRepository; @Autowired private MailService mailService; @Value("${trackr.frontendUrl}") private String frontendUrl; /** * Remind all employees of a federal state to track their working times via mail. Since holidays differ in different states this needs the state. * @param state The federal state to select employees from. */ public void remindEmployeesToTrackWorkTimes(FederalState state) { List allEmployees = employeeRepository.findByFederalState(state); allEmployees.forEach(employee -> { SimpleMailMessage mailMessage = getReminderMailMessage(employee); mailService.sendMail(mailMessage); }); } protected SimpleMailMessage getReminderMailMessage(Employee employee) { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo(employee.getEmail()); mailMessage.setFrom("no-reply@techdev.de"); mailMessage.setSubject("Track your working time"); mailMessage.setText("Please make sure to track your working time by the end of the month: " + frontendUrl + "/employee/timesheet"); return mailMessage; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/Project.java ================================================ package de.techdev.trackr.domain.project; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.domain.project.billtimes.BillableTime; import de.techdev.trackr.domain.project.worktimes.WorkTime; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.Min; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * @author Moritz Schulze */ @Entity @Getter @Setter @JsonIgnoreProperties({"workTimes", "billableTimes"}) public class Project { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotEmpty @Column(unique = true) private String identifier; @NotEmpty private String name; @ManyToOne @JoinColumn(name = "company_id") private Company company; @Min(0) private Integer volume; @Min(0) private BigDecimal hourlyRate; @Min(0) private BigDecimal dailyRate; @Min(0) private BigDecimal fixedPrice; @ManyToOne @JoinColumn(name = "debitor_id") private Company debitor; @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "project") private List workTimes = new ArrayList<>(); @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "project") private List billableTimes = new ArrayList<>(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/ProjectEventHandler.java ================================================ package de.techdev.trackr.domain.project; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; /** * @author Moritz Schulze */ @RepositoryEventHandler(Project.class) @SuppressWarnings("unused") public class ProjectEventHandler { @HandleBeforeSave @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkUpdatePermission(Project project) { } @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkCreatePermission(Project project) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkDeletePermission(Project project) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkCompanyPermission(Project project, Object link) { } @HandleBeforeLinkDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void checkLinkDeletePermission(Project project, Object linkedEntity) { } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/ProjectRepository.java ================================================ package de.techdev.trackr.domain.project; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import java.util.List; /** * @author Moritz Schulze */ public interface ProjectRepository extends JpaRepository { Project findByIdentifier(@Param("identifier") String identifier); List findByNameLikeIgnoreCaseOrIdentifierLikeIgnoreCaseOrderByNameAsc(@Param("name") String name, @Param("identifier") String identifier); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/ProjectWithCompanyAndDebitorProjection.java ================================================ package de.techdev.trackr.domain.project; import de.techdev.trackr.domain.company.Company; import org.springframework.data.rest.core.config.Projection; import java.math.BigDecimal; /** * @author Moritz Schulze */ @Projection(types = Project.class, name = "withCompanyAndDebitor") public interface ProjectWithCompanyAndDebitorProjection { Long getId(); Integer getVersion(); String getIdentifier(); String getName(); Company getCompany(); Integer getVolume(); BigDecimal getHourlyRate(); BigDecimal getDailyRate(); BigDecimal getFixedPrice(); Company getDebitor(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTime.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.project.Project; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import java.util.Date; /** * @author Moritz Schulze */ @Entity @Getter @Setter @Table(uniqueConstraints = @UniqueConstraint(columnNames = {"employee", "project", "date"})) public class BillableTime { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @ManyToOne @NotNull @JoinColumn(name = "employee") private Employee employee; @ManyToOne @NotNull @JoinColumn(name = "project") private Project project; @NotNull @Temporal(TemporalType.DATE) private Date date; @NotNull @Min(0) private Integer minutes; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeController.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.trackr.domain.project.Project; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.convert.ConversionService; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Date; import java.util.List; import java.util.Map; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.summingInt; /** * @author Moritz Schulze */ @Controller @RequestMapping("/billableTimes") public class BillableTimeController { @Autowired @Qualifier("defaultConversionService") private ConversionService conversionService; @Autowired private BillableTimeRepository billableTimeRepository; @PreAuthorize("hasRole('ROLE_SUPERVISOR')") @ResponseBody @RequestMapping(value = "/findEmployeeMappingByProjectAndDateBetween", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Map findEmployeeMappingByProjectAndDateBetween(@RequestParam("project") String projectId, @RequestParam("start") Date start, @RequestParam("end") Date end) { Project project = conversionService.convert(Long.valueOf(projectId), Project.class); List billableTimes = billableTimeRepository.findByProjectAndDateBetweenOrderByDateAsc(project, start, end); return billableTimes.stream().collect(groupingBy(bt -> bt.getEmployee().fullName(), summingInt(BillableTime::getMinutes))); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeEventHandler.java ================================================ package de.techdev.trackr.domain.project.billtimes; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; /** * @author Moritz Schulze */ @RepositoryEventHandler(BillableTime.class) @SuppressWarnings("unused") public class BillableTimeEventHandler { @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkCreateAuthority(BillableTime billableTime) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkUpdateAuthority(BillableTime billableTime) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkDeleteAuthority(BillableTime billableTime) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_SUPERVISOR')") public void checkLinkSaveAuthority(BillableTime billableTime, Object links) { } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void checkLinkDeleteAuthority(BillableTime billableTime, Object linkedEntity) { } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeRepository.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.trackr.domain.project.Project; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; import java.util.List; /** * @author Moritz Schulze */ public interface BillableTimeRepository extends CrudRepository { @Override @RestResource(exported = false) Iterable findAll(); @Override @RestResource(exported = false) Iterable findAll(Iterable longs); @Override @PreAuthorize("hasRole('ROLE_SUPERVISOR')") BillableTime findOne(Long aLong); @PreAuthorize("hasRole('ROLE_SUPERVISOR')") List findByProjectAndDateBetweenOrderByDateAsc(@Param("project") Project project, @Param("start") Date start, @Param("end") Date end); @PreAuthorize("hasRole('ROLE_ADMIN')") List findByDateBetween(@Param("start") Date start, @Param("end") Date end); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeWithProjectProjection.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.trackr.domain.project.Project; import org.springframework.data.rest.core.config.Projection; import java.util.Date; /** * @author Moritz Schulze */ @Projection(types = BillableTime.class, name = "withProject") public interface BillableTimeWithProjectProjection { Long getId(); Integer getVersion(); Project getProject(); Date getDate(); Integer getMinutes(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/ChangeStateService.java ================================================ package de.techdev.trackr.domain.project.invoice; import org.springframework.beans.factory.annotation.Autowired; /** * @author Moritz Schulze */ public class ChangeStateService { @Autowired private InvoiceRepository invoiceRepository; public Invoice changeState(Invoice invoice, Invoice.InvoiceState invoiceState) { invoice.setInvoiceState(invoiceState); return invoiceRepository.save(invoice); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/Invoice.java ================================================ package de.techdev.trackr.domain.project.invoice; import de.techdev.trackr.domain.company.Company; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import javax.persistence.*; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.Date; @Entity @Getter @Setter @Table(uniqueConstraints = @UniqueConstraint(columnNames = "identifier")) public class Invoice { public enum InvoiceState { OUTSTANDING, PAID, OVERDUE } @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @NotEmpty private String identifier; @NotNull @Temporal(TemporalType.DATE) private Date creationDate; @Min(0) private BigDecimal invoiceTotal; @ManyToOne @JoinColumn(name = "debitor") private Company debitor; @Temporal(TemporalType.DATE) private Date dueDate; @NotNull @Enumerated(EnumType.STRING) private InvoiceState invoiceState; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceController.java ================================================ package de.techdev.trackr.domain.project.invoice; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * @author Moritz Schulze */ @Controller @RequestMapping(value = "/invoices") public class InvoiceController { @Autowired private ChangeStateService changeStateService; @Autowired private InvoiceRepository invoiceRepository; @RequestMapping(value = "{id}/markPaid", method = RequestMethod.POST) @ResponseBody @PreAuthorize("hasRole('ROLE_ADMIN')") public String markAsPaid(@PathVariable("id") Long invoiceId) { Invoice invoice = invoiceRepository.findOne(invoiceId); changeStateService.changeState(invoice, Invoice.InvoiceState.PAID); return "Ok."; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceEventHandler.java ================================================ package de.techdev.trackr.domain.project.invoice; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.util.LocalDateUtil; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; import java.time.LocalDate; import static de.techdev.trackr.util.LocalDateUtil.fromLocalDate; /** * @author Moritz Schulze */ @RepositoryEventHandler(Invoice.class) @SuppressWarnings("unused") public class InvoiceEventHandler { @HandleBeforeCreate @PreAuthorize("hasRole('ROLE_ADMIN')") public void authorizeCreate(Invoice invoice) { setDueDateFromTimeForPayment(invoice); setInvoiceStateIfNecessary(invoice); } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_ADMIN')") public void authorizeUpdate(Invoice invoice) { setDueDateFromTimeForPayment(invoice); setInvoiceStateIfNecessary(invoice); } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN')") public void authorizeDelete(Invoice invoice) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_ADMIN')") public void linkSave(Invoice invoice, Object links) { if (links instanceof Company) { setDueDateFromTimeForPayment(invoice); setInvoiceStateIfNecessary(invoice); } } /** * Sets the invoice state to OVERDUE if the due date is before today. */ protected void setInvoiceStateIfNecessary(Invoice invoice) { if (invoice.getDueDate() != null) { LocalDate today = LocalDate.now(); if (invoice.getDueDate().before(fromLocalDate(today))) { invoice.setInvoiceState(Invoice.InvoiceState.OVERDUE); } else { invoice.setInvoiceState(Invoice.InvoiceState.OUTSTANDING); } } } /** * Set the due date automatically if it is not set and the debitor of the invoice has a timeForPayment. */ protected void setDueDateFromTimeForPayment(Invoice invoice) { if (invoice.getDueDate() == null && invoice.getDebitor() != null && invoice.getDebitor().getTimeForPayment() != null) { LocalDate creationDate = LocalDateUtil.fromDate(invoice.getCreationDate()); LocalDate dueDate = creationDate.plusDays(invoice.getDebitor().getTimeForPayment()); invoice.setDueDate(LocalDateUtil.fromLocalDate(dueDate)); } } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceOverdueService.java ================================================ package de.techdev.trackr.domain.project.invoice; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; import static de.techdev.trackr.util.LocalDateUtil.fromLocalDate; /** * Marks invoices as overdue if the due date is after today. * @author Moritz Schulze */ @Slf4j public class InvoiceOverdueService { @Autowired private InvoiceRepository invoiceRepository; /** * Mark all outstanding invoices with a due date after expiry date as overdue. * @param expiryDate The date to use as a bound. */ @Transactional public void markOverdueInvoices(LocalDate expiryDate) { List invoices = invoiceRepository.findByDueDateBeforeAndInvoiceState(fromLocalDate(expiryDate), Invoice.InvoiceState.OUTSTANDING); for (Invoice invoice : invoices) { log.info("Setting state to overdue on invoice {}", invoice); invoice.setInvoiceState(Invoice.InvoiceState.OVERDUE); invoiceRepository.save(invoice); } } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceRepository.java ================================================ package de.techdev.trackr.domain.project.invoice; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Date; import java.util.List; /** * @author Moritz Schulze */ public interface InvoiceRepository extends JpaRepository { @Override @PreAuthorize("hasRole('ROLE_ADMIN')") Invoice findOne(Long aLong); @Override @PreAuthorize("hasRole('ROLE_ADMIN')") Page findAll(Pageable pageable); @PreAuthorize("hasRole('ROLE_ADMIN')") Page findByInvoiceState(@Param("state") Invoice.InvoiceState state, Pageable pageable); @PreAuthorize("hasRole('ROLE_ADMIN')") Page findByIdentifierLikeIgnoreCaseAndInvoiceState(@Param("identifier") String identifier, @Param("state") Invoice.InvoiceState state, Pageable pageable); @RestResource(exported = false) List findByDueDateBeforeAndInvoiceState(Date date, Invoice.InvoiceState invoiceState); @PreAuthorize("hasRole('ROLE_ADMIN')") List findByCreationDateBetween(@Param("start") Date start, @Param("end") Date end, @Param("sort") Sort sort); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceScheduledJob.java ================================================ package de.techdev.trackr.domain.project.invoice; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import java.time.LocalDate; /** * @author Moritz Schulze */ public class InvoiceScheduledJob { @Autowired private InvoiceOverdueService invoiceOverdueService; @Scheduled(cron = "0 0 1 * * *") public void markOverdueInvoices() { LocalDate today = LocalDate.now(); invoiceOverdueService.markOverdueInvoices(today); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceWithDebitorProjection.java ================================================ package de.techdev.trackr.domain.project.invoice; import de.techdev.trackr.domain.company.Company; import org.springframework.data.rest.core.config.Projection; import java.math.BigDecimal; import java.util.Date; /** * @author Moritz Schulze */ @Projection(types = Invoice.class, name = "withDebitor") public interface InvoiceWithDebitorProjection { Long getId(); Integer getVersion(); String getIdentifier(); Date getCreationDate(); BigDecimal getInvoiceTotal(); Company getDebitor(); Date getDueDate(); Invoice.InvoiceState getInvoiceState(); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/CustomWorkTime.java ================================================ package de.techdev.trackr.domain.project.worktimes; import lombok.Getter; import lombok.Setter; import java.time.Duration; import java.util.Date; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.reducing; /** * DTO that contains only the needed information for the method findEmployeeMappingByProjectAndDateBetween. */ @Getter @Setter public class CustomWorkTime implements Comparable { private Date date; private Long enteredMinutes; private Double hours; private Long billedTimeId; private String comment; /** * Add up work times that belong to the same date. * * @param workTimes The worktimes to add * @return A sorted list of worktimes. */ public static List reduceAndSortWorktimes(List workTimes) { CustomWorkTime identity = new CustomWorkTime(); identity.setEnteredMinutes(0L); Map mapped = workTimes.stream().collect(groupingBy(CustomWorkTime::getDate, reducing(identity, CustomWorkTime::addOtherWorkTime))); return mapped.values().stream().sorted().collect(Collectors.toList()); } private CustomWorkTime addOtherWorkTime(CustomWorkTime other) { CustomWorkTime added = new CustomWorkTime(); added.setDate(other.getDate()); added.setEnteredMinutes(this.getEnteredMinutes() + other.getEnteredMinutes()); added.addComment(this.getComment()); added.addComment(other.getComment()); return added; } private void addComment(String comment) { if (getComment() == null) { setComment(comment); } else if(comment != null) { setComment(this.comment + "\n" + comment); } } public static CustomWorkTime valueOf(WorkTime workTime) { CustomWorkTime customWorkTime = new CustomWorkTime(); customWorkTime.enteredMinutes = Duration.between(workTime.getStartTime().toLocalTime(), workTime.getEndTime().toLocalTime()).toMinutes(); customWorkTime.date = workTime.getDate(); customWorkTime.comment = workTime.getComment(); return customWorkTime; } @Override public int compareTo(CustomWorkTime o) { return this.getDate().compareTo(o.getDate()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CustomWorkTime that = (CustomWorkTime) o; return !(date != null ? !date.equals(that.date) : that.date != null); } @Override public int hashCode() { return date != null ? date.hashCode() : 0; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/Projections.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.project.Project; import org.springframework.data.rest.core.config.Projection; import java.sql.Time; import java.util.Date; /** * @author Moritz Schulze */ public class Projections { @Projection(types = WorkTime.class, name = "withEmployee") public interface WorkTimeWithEmployeeProjection { Long getId(); Integer getVersion(); Employee getEmployee(); Date getDate(); Time getStartTime(); Time getEndTime(); String getComment(); } @Projection(types = WorkTime.class, name = "withProject") public interface WorkTimeWithProjectProjection { Long getId(); Integer getVersion(); Project getProject(); Date getDate(); Time getStartTime(); Time getEndTime(); String getComment(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTime.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.project.Project; import de.techdev.trackr.domain.validation.constraints.EndAfterBegin; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.sql.Time; import java.util.Date; /** * @author Moritz Schulze */ @Entity @Getter @Setter @EndAfterBegin(begin = "startTime", end = "endTime") public class WorkTime { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private Integer version; @ManyToOne @NotNull @JoinColumn(name = "employee") private Employee employee; @ManyToOne @NotNull @JoinColumn(name = "project") private Project project; @NotNull @Temporal(TemporalType.DATE) private Date date; private Time startTime; private Time endTime; private String comment; } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeController.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.project.Project; import de.techdev.trackr.domain.project.billtimes.BillableTime; import de.techdev.trackr.domain.project.billtimes.BillableTimeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.convert.ConversionService; import org.springframework.hateoas.EntityLinks; import org.springframework.hateoas.Link; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import static java.util.stream.Collectors.*; /** * @author Moritz Schulze */ @Controller @RequestMapping("/workTimes") public class WorkTimeController { @Autowired @Qualifier("defaultConversionService") private ConversionService conversionService; @Autowired private WorkTimeRepository workTimeRepository; @Autowired private BillableTimeRepository billableTimeRepository; @Autowired protected EntityLinks repositoryEntityLinks; /** * Finds all workTimes for a project in a given interval and converts them to a mapping of employee id to worktimes. * * @param projectId The id of the project * @param start The start of the interval * @param end The end of the interval * @return The mapping of employee id to a DTO object that contains the employee along the work times. */ @PreAuthorize("hasRole('ROLE_SUPERVISOR')") @RequestMapping(value = "/findEmployeeMappingByProjectAndDateBetween", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Map findEmployeeMappingByProjectAndDateBetween( @RequestParam("project") String projectId, @RequestParam("start") Date start, @RequestParam("end") Date end) { //TODO: Make spring do the conversion automatically Project project = conversionService.convert(Long.valueOf(projectId), Project.class); List workTimes = workTimeRepository.findByProjectAndDateBetweenOrderByDateAscStartTimeAsc(project, start, end); return convertStreamOfWorkTimesToMap(workTimes, getBilledMinutesMapping(start, end, project)); } /** * Maps employees via id to the already billed times in a given interval. * * @param start The start of the interval * @param end The end of the interval * @param project The project * @return A map of employee id to a map of date to billable times. */ protected Map> getBilledMinutesMapping(Date start, Date end, Project project) { List billableTimes = billableTimeRepository.findByProjectAndDateBetweenOrderByDateAsc(project, start, end); return billableTimes.stream().collect( groupingBy(bt -> bt.getEmployee().getId(), mapping(billableTime -> billableTime, toMap(BillableTime::getDate, Function.identity())))); } /** * Takes a list of workTimes, groups them by employee while transforming the WorkTime objects to CustomWorkTime DTOs and afterwards maps it * to a map of long (employee id) to the WorkTimeEmployee DTO. * * @param workTimes The list of worktimes to transform * @param billedMinutesMapping Mapping of already billed minutes * @return The mapping of Long to WorkTimeEmployee */ protected Map convertStreamOfWorkTimesToMap(List workTimes, Map> billedMinutesMapping) { return workTimes.stream().collect( groupingBy( WorkTime::getEmployee, mapping(CustomWorkTime::valueOf, toList()) )).entrySet().stream().collect( HashMap::new, (resultMap, entry) -> { Link link = repositoryEntityLinks.linkToSingleResource(Employee.class, entry.getKey().getId()); WorkTimeEmployee workTimeEmployee = WorkTimeEmployee.valueOf(entry.getKey(), entry.getValue()); workTimeEmployee.addBilledMinutes(billedMinutesMapping.get(entry.getKey().getId())); workTimeEmployee.add(link.withSelfRel()); resultMap.put(entry.getKey().getId(), workTimeEmployee); }, HashMap::putAll); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeEmployee.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.project.billtimes.BillableTime; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.springframework.hateoas.ResourceSupport; import java.util.Date; import java.util.List; import java.util.Map; /** * DTO that contains only the needed information for the method findEmployeeMappingByProjectAndDateBetween. *

* It extends {@link org.springframework.hateoas.ResourceSupport} so a link to the employee entity can be added. */ @Getter @Setter @EqualsAndHashCode(callSuper = false) public class WorkTimeEmployee extends ResourceSupport { private String name; private List workTimes; /** * Create a WorkTimeEmployee out of an Employee and a list of workTimes. It is the responsibility of the caller to * assure that the workTimes belong to the employee. *

* This method will aggregate the workTimes by date and sum up the worked hours. * * @param employee The employee to use * @param workTimes The list of workTimes to use. * @return A workTime employee */ public static WorkTimeEmployee valueOf(Employee employee, List workTimes) { WorkTimeEmployee workTimeEmployee = new WorkTimeEmployee(); workTimeEmployee.name = employee.fullName(); workTimeEmployee.workTimes = CustomWorkTime.reduceAndSortWorktimes(workTimes); return workTimeEmployee; } public void addBilledMinutes(Map dateBillableTimeMapping) { workTimes.forEach(ctw -> { if(dateBillableTimeMapping != null && dateBillableTimeMapping.get(ctw.getDate()) != null) { ctw.setBilledTimeId(dateBillableTimeMapping.get(ctw.getDate()).getId()); ctw.setHours(dateBillableTimeMapping.get(ctw.getDate()).getMinutes().doubleValue() / 60); } }); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeEventHandler.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import org.springframework.data.rest.core.annotation.*; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.HttpRequestMethodNotSupportedException; @RepositoryEventHandler(WorkTime.class) @SuppressWarnings("unused") public class WorkTimeEventHandler { @HandleBeforeCreate @PreAuthorize("#workTime.employee.email == principal?.username") public void checkCreateAuthority(WorkTime workTime) { } @HandleBeforeSave @PreAuthorize("hasRole('ROLE_ADMIN') or #workTime.employee.email == principal?.username") public void checkUpdateAuthority(WorkTime workTime) { } @HandleBeforeDelete @PreAuthorize("hasRole('ROLE_ADMIN') or #workTime.employee.email == principal?.username") public void checkDeleteAuthority(WorkTime workTime) { } @HandleBeforeLinkSave @PreAuthorize("hasRole('ROLE_ADMIN') or #workTime.employee.email == principal?.username") public void checkUpdateLinkAuthority(WorkTime workTime, Object link) throws HttpRequestMethodNotSupportedException { if(Employee.class.isAssignableFrom(link.getClass())) { throw new HttpRequestMethodNotSupportedException("POST", new String[0]); } } @HandleBeforeLinkDelete @PreAuthorize("denyAll()") public void checkDeleteLinkAuthority(WorkTime workTime, Object linkedEntity) { //deny all, cannot be called } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeRepository.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.project.Project; import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import javax.persistence.TemporalType; import java.util.Date; import java.util.List; public interface WorkTimeRepository extends CrudRepository { @Override @RestResource(exported = false) List findAll(); @Override @RestResource(exported = false) List findAll(Iterable longs); @Override @PostAuthorize("hasRole('ROLE_SUPERVISOR') or returnObject.employee.email == principal?.username") WorkTime findOne(Long aLong); @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #employee.email == principal?.username") List findByEmployeeAndDateOrderByStartTimeAsc(@Param("employee") Employee employee, @Param("date") @Temporal(TemporalType.DATE) Date date); @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #employee.email == principal?.username") List findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc(@Param("employee") Employee employee, @Param("start") @Temporal(TemporalType.DATE) Date start, @Param("end") @Temporal(TemporalType.DATE) Date end); @PreAuthorize("hasRole('ROLE_SUPERVISOR')") List findByProjectAndDateBetweenOrderByDateAscStartTimeAsc(@Param("project") Project project, @Param("start") @Temporal(TemporalType.DATE) Date start, @Param("end") @Temporal(TemporalType.DATE) Date end); @PreAuthorize("hasRole('ROLE_ADMIN')") List findByDateBetween(@Param("start") Date start, @Param("end") Date end); } ================================================ FILE: src/main/java/de/techdev/trackr/domain/scheduling/LastWorkdayDayOfMonthTrigger.java ================================================ package de.techdev.trackr.domain.scheduling; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.employee.vacation.HolidayRepository; import de.techdev.trackr.util.LocalDateUtil; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import static de.techdev.trackr.util.LocalDateUtil.fromDate; import static de.techdev.trackr.util.LocalDateUtil.fromLocalDate; /** * A trigger that is executed on the last workday in the month. * * @author Moritz Schulze */ @Setter @Slf4j public class LastWorkdayDayOfMonthTrigger implements Trigger { @Autowired private HolidayRepository holidayRepository; private FederalState federalState; /** * Get a list of all holidays for a state and month as {@link java.time.LocalDate}. * * @param state The federal state * @param month The month * @return The list of public holidays in the state in the month. */ protected List getHolidaysForMonth(FederalState state, LocalDate month) { LocalDate firstDayOfMonth = month.with(TemporalAdjusters.firstDayOfMonth()); LocalDate lastDayOfMonth = month.with(TemporalAdjusters.lastDayOfMonth()); return holidayRepository .findByFederalStateAndDayBetween(state, fromLocalDate(firstDayOfMonth), fromLocalDate(lastDayOfMonth)) .stream().map(holiday -> fromDate(holiday.getDay())) .collect(Collectors.toList()); } /** * Get the last day in month that is not a saturday or sunday or public holiday. * * @param month The month * @param holidaysInThisMonth A list of holidays for the given month * @return New date with the last day that is not saturday or sunday or public holiday in the month. */ protected LocalDate lastWeekdayInMonth(LocalDate month, List holidaysInThisMonth) { LocalDate returnDate = month.with(TemporalAdjusters.lastDayOfMonth()); while (!isWorkday(returnDate, holidaysInThisMonth)) { returnDate = returnDate.minusDays(1); } return returnDate; } /** * Decides whether a date is a work day or not (i.e. not a saturday, sunday or a public holiday). * * @param date The date to check * @param holidays A list of holidays to use * @return true if the day is not a sunday, saturday or in the list of holidays. */ protected boolean isWorkday(LocalDate date, List holidays) { return date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY && !holidays.contains(date); } @Override public Date nextExecutionTime(TriggerContext triggerContext) { return fromLocalDate(nextExecutionTimeInternal(triggerContext)); } protected LocalDate nextExecutionTimeInternal(TriggerContext triggerContext) { LocalDate now = LocalDate.now(); if (triggerContext.lastScheduledExecutionTime() != null && LocalDateUtil.fromDate(triggerContext.lastScheduledExecutionTime()).getMonth() == now.getMonth()) { now = now.plusMonths(1); } LocalDate nextExecutionTime = lastWeekdayInMonth(now, getHolidaysForMonth(federalState, now)); log.debug("Trigger for state {} sets next execution date to {}", federalState, nextExecutionTime); return nextExecutionTime; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/scheduling/ScheduledJobsConfiguration.java ================================================ package de.techdev.trackr.domain.scheduling; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.employee.EmployeeScheduledJob; import de.techdev.trackr.domain.employee.vacation.VacationRequestScheduledJobs; import de.techdev.trackr.domain.project.invoice.InvoiceScheduledJob; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import static java.util.Arrays.asList; /** * @author Moritz Schulze */ @Configuration @EnableScheduling public class ScheduledJobsConfiguration implements SchedulingConfigurer { @Bean public VacationRequestScheduledJobs vacationScheduledJobs() { return new VacationRequestScheduledJobs(); } @Bean public EmployeeScheduledJob employeeScheduledJob() { return new EmployeeScheduledJob(); } @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public LastWorkdayDayOfMonthTrigger lastWorkdayDayOfMonthTrigger() { return new LastWorkdayDayOfMonthTrigger(); } @Bean public InvoiceScheduledJob invoiceScheduledJob() { return new InvoiceScheduledJob(); } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); for (FederalState federalState : FederalState.values()) { LastWorkdayDayOfMonthTrigger trigger = lastWorkdayDayOfMonthTrigger(); trigger.setFederalState(federalState); taskRegistrar.addTriggerTask(employeeScheduledJob().sendWorkTimeReminderTask(federalState), trigger); } } /** * @return An executor that has admin rights. */ @Bean(destroyMethod = "shutdownNow") public Executor taskExecutor() { ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor(); SecurityContext schedulerContext = createSchedulerSecurityContext(); return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext); } /** * @return A security context with an admin authentication token. */ private SecurityContext createSchedulerSecurityContext() { SecurityContext context = SecurityContextHolder.createEmptyContext(); List grantedAuthorities = asList((GrantedAuthority) () -> "ROLE_ADMIN"); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin@techdev.de", "", grantedAuthorities); context.setAuthentication(authenticationToken); return context; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/translations/TranslationController.java ================================================ package de.techdev.trackr.domain.translations; import de.techdev.trackr.domain.employee.Settings; import de.techdev.trackr.domain.employee.SettingsRepository; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.LocaleResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.Principal; import java.util.Locale; @Controller @RequestMapping("/translations") @Slf4j public class TranslationController { @Autowired private LocaleResolver localeResolver; @Autowired private SettingsRepository settingsRepository; @RequestMapping(method = RequestMethod.GET) public void getTranslations(Locale locale, HttpServletResponse response) { ClassPathResource translationFile = new ClassPathResource("/i18n/trackr-" + locale.toLanguageTag() + ".json"); response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); try { IOUtils.copy(translationFile.getInputStream(), response.getWriter(), "UTF-8"); response.setStatus(200); response.getWriter().close(); } catch (IOException e) { throw new IllegalStateException("Could not open translation file", e); } } @RequestMapping(method = RequestMethod.PUT, produces = MediaType.TEXT_PLAIN_VALUE) @ResponseBody public String setLocale(@RequestParam("locale") Locale locale, HttpServletRequest request, HttpServletResponse response, Principal principal) { localeResolver.setLocale(request, response, locale); Settings localeSettings = settingsRepository.findByTypeAndEmployee_Email(Settings.SettingsType.LOCALE, principal.getName()); if (localeSettings == null) { log.error("Employee {} without locale settings.", principal.getName()); return "Ok."; } localeSettings.setValue(locale.getLanguage()); settingsRepository.save(localeSettings); return "Ok."; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/validation/EndAfterBeginValidator.java ================================================ package de.techdev.trackr.domain.validation; import de.techdev.trackr.domain.validation.constraints.EndAfterBegin; import org.springframework.beans.DirectFieldAccessor; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Date; /** * @author Moritz Schulze */ public class EndAfterBeginValidator implements ConstraintValidator { private String beginFieldName; private String endFieldName; private String messageTemplate; @Override public void initialize(EndAfterBegin constraintAnnotation) { beginFieldName = constraintAnnotation.begin(); endFieldName = constraintAnnotation.end(); messageTemplate = constraintAnnotation.message(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { DirectFieldAccessor dfa = new DirectFieldAccessor(value); Class beginFieldType = dfa.getPropertyType(beginFieldName); Class endFieldType = dfa.getPropertyType(endFieldName); if(! (Date.class.isAssignableFrom(beginFieldType) || Date.class.isAssignableFrom(endFieldType)) ) { throw new IllegalArgumentException("Fields are not date objects."); } Date beginField = (Date) dfa.getPropertyValue(beginFieldName); Date endField = (Date) dfa.getPropertyValue(endFieldName); boolean isValid = beginField == null || endField == null || !beginField.after(endField); if (!isValid) { context.disableDefaultConstraintViolation(); context. buildConstraintViolationWithTemplate(messageTemplate) .addNode(endFieldName).addConstraintViolation(); } return isValid; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/validation/ProjectBelongsToCompanyValidator.java ================================================ package de.techdev.trackr.domain.validation; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.domain.project.Project; import de.techdev.trackr.domain.validation.constraints.ProjectBelongsToCompany; import org.springframework.beans.DirectFieldAccessor; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * @author Moritz Schulze */ public class ProjectBelongsToCompanyValidator implements ConstraintValidator { private String projectFieldName; private String companyFieldName; private String messageTemplateName; @Override public void initialize(ProjectBelongsToCompany constraintAnnotation) { companyFieldName = constraintAnnotation.companyField(); projectFieldName = constraintAnnotation.projectField(); messageTemplateName = constraintAnnotation.message(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { boolean isValid = true; DirectFieldAccessor dfa = new DirectFieldAccessor(value); Object project = dfa.getPropertyValue(projectFieldName); if (project != null) { Company company = (Company) dfa.getPropertyValue(companyFieldName); isValid = ((Project) project).getCompany().getId().equals(company.getId()); } if (!isValid) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(messageTemplateName) .addNode(projectFieldName).addConstraintViolation(); } return isValid; } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/validation/constraints/EndAfterBegin.java ================================================ package de.techdev.trackr.domain.validation.constraints; import de.techdev.trackr.domain.validation.EndAfterBeginValidator; import javax.validation.Constraint; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Checks that the begin field is not after the end date, but they may be the same. * * @author Moritz Schulze */ @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = EndAfterBeginValidator.class) public @interface EndAfterBegin { Class[] groups() default {}; Class[] payload() default {}; String message() default "{validation.date.endAfterBegin}"; String begin(); String end(); @interface List { EndAfterBegin[] value(); } } ================================================ FILE: src/main/java/de/techdev/trackr/domain/validation/constraints/ProjectBelongsToCompany.java ================================================ package de.techdev.trackr.domain.validation.constraints; import de.techdev.trackr.domain.validation.ProjectBelongsToCompanyValidator; import javax.validation.Constraint; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Validation that checks if a project field, if it is not null, belongs to a company field. * * @author Moritz Schulze */ @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = ProjectBelongsToCompanyValidator.class) public @interface ProjectBelongsToCompany { Class[] groups() default {}; Class[] payload() default {}; String message() default "{validation.company.projectBelongsTo}"; String companyField() default "company"; String projectField() default "project"; } ================================================ FILE: src/main/java/de/techdev/trackr/util/LocalDateUtil.java ================================================ package de.techdev.trackr.util; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; /** * Convert {@link java.util.Date} from and to {@link java.time.LocalDate} * @author Moritz Schulze */ public final class LocalDateUtil { private LocalDateUtil() { } public static Date fromLocalDate(LocalDate date) { Instant instant = date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); return Date.from(instant); } public static LocalDate fromDate(Date date) { Instant instant = Instant.ofEpochMilli(date.getTime()); return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate(); } } ================================================ FILE: src/main/resources/META-INF/mail-integration.xml ================================================ ================================================ FILE: src/main/resources/banner.txt ================================================ ........... ............................ .......................... ..... ................................ .... .,..................................... ..... .................................................. ...................................... ................................... ................................ .............................. ...,,,,,,,,.... .............................. ,**////////////////////*,. ....................,,,...... .*//////////////////////////////,.. ....,,,(###%%%%%%%####((//*,,,..... .,///////////////////////////////////,,,,,,*******(#%%%%%%%%%%%%%%%%%##(/*,... .///////////////////////////////((##%%%%#***********/%%%%%%%%%%%%%%%%%%%%%%%#(,.. .*///////////////////////////((#%%%%%%%%%%%#/*********#%%%%%%%%%%%%%%%%%%%%%%%%%#/ .*///////////////////////((#%%%%%%%%%%%%%%%%#*********/#%%%%%%%%%%%%%%%%%%%%%%%%%%(, */////////////////////((#%%%%%%%%%%%%%%%%%%%/********/#%%%%%%%%%%%%%%%%%%%%%%%%%%#* .///////////////////(#%%%%%%%%%%%%%%%%%%%%%%(********/(%%%%%%%%%%%%%%%%%%%%%%%%%%%(, *////////////////#%%%%%%%%%%%%%%%%%%%%%%%%%/********/#%%%%%%%%%%%%%%%%%%%%%%%%%%%#* .*/////////////(#%%%%%%%%%%%%%%%%%%%%%%%%%%*********/#%%%%%%%%%%%%%%%%%%%%%%%%%%%#, .*//////////(#%%%%%%%%%%%%%%%%%%%%%%%%%%#(*********#%%%%%%%%%%%%%%%%%%%%%%%%%%%%/. *///////(#%%%%%%%%%%%%%%%%%%%%%%%%%%%#/*********/%%%%%%%%%%%%%%%%%%%%%%%%%%%%(, .*//////#%%%%%%%%%%%%%%%%%%%%%%%%%%%%/**********(%%%%%%%%%%%%%%%%%%%%%%%%%%%%, .////(%%%%%%%%%%%%%%%%%%%%%%%%%%#(/*********/#%%%%%%%%%%%%%%%%%%%%%%%%%%%#/ ,*(#%%%%%%%%%%%%%%%%%%%%%%%%%#***********/%%%%%%%%%%%%%%%%%%%%%%%%%%%%/. *(%%%%%%%%%%%%%%%%%%%%%%#/***********/#%%%%%%%%%%%%%%%%%%%%%%%%%%%(* /#%%%%%%%%%%%%%%%%%%#(/************#%%%%%%%%%%%%%%%%%%%%%%%%%%%*. ,/(#%%%%%%%%##(/*************/#%%%%%%%%%%%%%%%%%%%%%%%%%%#, ,*****************/(%%%%%%%%%%%%%%%%%%%%%%%%%#, .,**************##%%%%%%%%%%%%%%%%%%%%%%%#*, .,********##%%%%%%%%%%%%%%%%%%%%%%#/. ,*/#%%%%%%%%%%%%%%%%%%%%##*,. .*/(##%%%%%%%%%%##(/*.. ........,,,,,,,,,,,,,,,,,......... ............................. ================================================ FILE: src/main/resources/data.sql ================================================ CREATE TABLE IF NOT EXISTS uuid_mapping (id int, uuid varchar); /* EMPLOYEE ADDRESSES */ INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (19, 0, 'Berliner Strasse', '94', '10117', 'Berlin', 'Deutschland'); INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (20, 0, 'Frankfurter Strasse', '1a', '10318', 'Berlin', 'Deutschland'); INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (21, 0, 'Architektenallee', '666', '76123', 'Karlsruhe', 'Deutschland'); INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (22, 0, 'Holzweg', '5', '64291', 'Darmstadt', 'Deutschland'); INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (23, 0, 'Ahornallee', '187', '10589', 'Berlin', 'Deutschland'); INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (24, 0, 'Markgrafenstrasse', '79', '15230', 'Berlin', 'Deutschland'); INSERT INTO address (id, version, street, housenumber, zipCode, city, country) VALUES (25, 0, 'Kurfuerstendamm', '461', '10234', 'Berlin', 'Deutschland'); /* EMPLOYEES */ INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, address_id, deleted) VALUES (1, 0, 'moritz.schulze@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 50000, 'BERLIN', '2014-02-01', 30, 19, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, address_id, deleted) VALUES (2, 0, 'markus.kappel@techdev.de', 'Adam', 'Smith', 'Software Consultant', 85, 55000, 'BERLIN', '2014-07-01', 20, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, address_id, deleted) VALUES (3, 0, 'alexander.hanschke@techdev.de', 'William', 'Hanson', 'CEO', 85, 60000, 'BADEN_WUERTTEMBERG', '2013-09-01', 21, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, address_id, deleted) VALUES (4, 0, 'adrian.krion@techdev.de','Bill', 'Rust', 'Processes Consultant', 87, 58000, 'HESSEN', '2014-01-01', 22, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, deleted) VALUES (5, 0, 'angelika.gutjahr@techdev.de', 'Jane', 'Dafoe', 'HR', 0, 500000, 'BADEN_WUERTTEMBERG', '2013-10-01', false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, address_id, deleted) VALUES (6, 0, 'nikolaj.weise@techdev.de', 'Horace', 'Nottingham', 'External Consultant', 90, 0, 'BERLIN', 23, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, address_id, deleted) VALUES (7, 0, 'jochen.toepfer@techdev.de', 'Vladimir', 'Wichowsko', 'Software Engineer', 80, 49000, 'BERLIN', '2013-11-01', 24, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, address_id, deleted) VALUES (8, 0, 'mehmed.halilovic@techdev.de', 'John', 'Hooper', 'Software Architect', 90, 65000, 'BERLIN', '2014-09-01', 25, false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, deleted) VALUES (9, 0, 'noop3@techdev.de', 'Todd', 'Floyd', 'HR', 0, 45000, 'BADEN_WUERTTEMBERG', '2014-03-16', false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, leaveDate, deleted) VALUES (10, 0, 'noop4@techdev.de', 'Joanne', 'Doughty', 'Practicant', 0, 25000, 'SCHLESWIG_HOLSTEIN', '2013-11-01', '2013-12-15', false); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, deleted) VALUES (11, 0, 'supervisor@techdev.de', 'Sean', 'Robinson', 'Software Engineer', 75, 51000, 'MECKLENBURG_VORPOMMERN', '2014-05-01', false); INSERT INTO settings (id, type, value, employee_id) VALUES (0, 'LOCALE', 'en', 1); INSERT INTO settings (id, type, value, employee_id) VALUES (1, 'LOCALE', 'de', 2); INSERT INTO settings (id, type, value, employee_id) VALUES (2, 'LOCALE', 'de', 3); INSERT INTO settings (id, type, value, employee_id) VALUES (3, 'LOCALE', 'en', 4); INSERT INTO settings (id, type, value, employee_id) VALUES (4, 'LOCALE', 'en', 5); INSERT INTO settings (id, type, value, employee_id) VALUES (5, 'LOCALE', 'en', 6); INSERT INTO settings (id, type, value, employee_id) VALUES (6, 'LOCALE', 'en', 7); INSERT INTO settings (id, type, value, employee_id) VALUES (7, 'LOCALE', 'en', 8); INSERT INTO settings (id, type, value, employee_id) VALUES (8, 'LOCALE', 'en', 9); INSERT INTO settings (id, type, value, employee_id) VALUES (9, 'LOCALE', 'en', 10); INSERT INTO settings (id, type, value, employee_id) VALUES (10, 'LOCALE', 'en', 11); /* (COMPANY) ADDRESSES */ INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (15, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (16, 0, 'Berliner Straße', '125', '60139', 'Frankfurt', 'Deutschland'); INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (17, 0, 'Holzweg', '2', '10521', 'Berlin', 'Deutschland'); INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (18, 0, 'Temple Road', '67', '40931', 'Düsseldorf', 'Deutschland'); /* COMPANIES */ INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (19, 0, 1000, 'webshop Ltd.', 15, 30); INSERT INTO company (id, version, companyId, name, address_id) VALUES (20, 0, 1001, 'finance Meier & partners', 16); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (21, 0, 1002, 'Origins', 17, 14); INSERT INTO company (id, version, companyId, name, address_id) VALUES (22, 0, 1003, 'scalar deployment GmbH', 18); /* CONTACT PERSONS */ INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, company, roles) VALUES (23, 0, 'Robert', 'Lake', 'r.lake@webshop.de', 'Mr', '0178/11234566', 19, 'Sales Manager'); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, company, roles) VALUES (24, 0, 'Lori', 'Carter', 'l.carter@webshop.de', 'Mrs', '0178/68203493', 19, 'Project Managerin, CIO'); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, company) VALUES (25, 0, 'Loyd', 'Boyden', 'loyd.boyden@fi-meyer.com', 'Mr', '0152 12039411', 20); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, company) VALUES (26, 0, 'Jimmy', 'Green', 'jim.green@origins.de', 'Mr', '0176/60012331', 21); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, company) VALUES (27, 0, 'Nichole', 'Morgan', 'n.morgan@scalard.net', 'Mrs', '0143/99682738', 22); /* PROJECTS */ INSERT INTO project (id, version, identifier, name, company_id, volume, fixedPrice, debitor_id) VALUES (28, 0, '1000.2014.1', 'Webshop - Checkout Development', 19, 60, 15000, 20); INSERT INTO project (id, version, identifier, name, company_id, volume, hourlyRate) VALUES (29, 0, '1000.2013.1', 'Process automization', 19, 30, 80); INSERT INTO project (id, version, identifier, name, company_id, volume, dailyRate) VALUES (30, 0, '1002.2013.1', 'IaaS Java Development', 21, 100, 670); INSERT INTO project (id, version, identifier, name, company_id, volume, hourlyRate) VALUES (31, 0, '1002.2013.2', 'Test Manager', 21, 50, 85); INSERT INTO project (id, version, identifier, name, company_id, volume, hourlyRate) VALUES (32, 0, '1002.2014.1', 'Frontend Development', 21, 30, 70); INSERT INTO project (id, version, identifier, name, company_id, volume, hourlyRate, debitor_id) VALUES (33, 0, '1003.2014.1', 'ESB Architecture', 22, 90, 80, 20); /* TRAVEL EXPENSE REPORTS */ INSERT INTO travelExpenseReport (id, version, employee_id, status, debitor_id) VALUES (34, 0, 1, 'PENDING', 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (35, 0, 1, 'SUBMITTED', '2014-06-01 19:41:31', 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (36, 0, 1, 'APPROVED', '2014-04-01 20:01:21', '2014-04-04 15:40:00', 8, 21); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (37, 0, 1, 'REJECTED', '2014-05-01 17:45:21', 22); INSERT INTO travelExpenseReport (id, version, employee_id, status, debitor_id) VALUES (38, 0, 2, 'PENDING', 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (39, 0, 2, 'SUBMITTED', '2014-06-01 13:30:21', 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (40, 0, 2, 'APPROVED', '2014-04-01 13:30:21', '2014-04-06 08:30:12', 3, 21); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (41, 0, 2, 'REJECTED', '2014-05-01 13:30:21', 22); INSERT INTO travelExpenseReport (id, version, employee_id, status, debitor_id) VALUES (42, 0, 3, 'PENDING', 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (43, 0, 3, 'SUBMITTED', '2014-06-01 13:30:21', 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (44, 0, 3, 'APPROVED', '2014-04-01 13:30:21', '2014-04-01 18:20:53', 1, 21); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (45, 0, 3, 'REJECTED', '2014-05-01 13:30:21', 22); INSERT INTO travelExpenseReport (id, version, employee_id, status, debitor_id) VALUES (46, 0, 4, 'PENDING', 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (47, 0, 4, 'SUBMITTED', '2014-06-01 13:30:21', 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (48, 0, 4, 'APPROVED', '2014-04-01 13:30:21', '2014-04-05 14:00:12', 8, 21); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (49, 0, 4, 'REJECTED', '2014-05-01 13:30:21', 22); INSERT INTO travelExpenseReport (id, version, employee_id, status, debitor_id) VALUES (50, 0, 5, 'PENDING', 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (51, 0, 5, 'SUBMITTED', '2014-06-01 13:30:21', 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (52, 0, 5, 'APPROVED', '2014-04-01 13:30:21', '2014-04-02 10:30:12', 7, 21); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (53, 0, 5, 'REJECTED', '2014-05-01 13:30:21', 22); INSERT INTO travelExpenseReport (id, version, employee_id, status, debitor_id) VALUES (54, 0, 6, 'PENDING', 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (55, 0, 6, 'SUBMITTED', '2014-06-01 13:30:21', 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (56, 0, 6, 'APPROVED', '2014-04-01 13:30:21', '2014-04-02 10:30:12', 5, 21); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (57, 0, 6, 'REJECTED', '2014-05-01 13:30:21', 22); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (58, 0, 7, 'APPROVED', '2013-11-01 13:30:21', '2013-11-02 10:30:12', 8, 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (59, 0, 7, 'APPROVED', '2013-12-01 13:30:21', '2013-12-04 10:30:12', 8, 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (60, 0, 7, 'APPROVED', '2014-01-01 13:30:21', '2014-01-07 10:30:12', 8, 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (61, 0, 7, 'APPROVED', '2014-02-01 13:30:21', '2014-02-10 10:30:12', 8, 19); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (62, 0, 11, 'APPROVED', '2014-05-01 13:30:21', '2014-05-02 10:30:12', 8, 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (63, 0, 11, 'APPROVED', '2014-06-01 13:30:21', '2014-06-02 10:30:12', 8, 20); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, approvalDate, approver_id, debitor_id) VALUES (64, 0, 11, 'APPROVED', '2014-07-01 13:30:21', '2014-07-02 10:30:12', 8, 20); /* WORKTIMES project 1000.2014.1 EMPLOYEE 1 */ INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (1, 0, 28, 1, '2014-06-02', '09:00:00', '17:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (2, 0, 28, 1, '2014-06-03', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (3, 0, 28, 1, '2014-06-04', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (4, 0, 28, 1, '2014-06-05', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (5, 0, 28, 1, '2014-06-06', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (6, 0, 28, 1, '2014-06-09', '09:00:00', '17:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (7, 0, 28, 1, '2014-06-10', '09:00:00', '17:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (8, 0, 28, 1, '2014-06-11', '08:30:00', '16:45:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (9, 0, 28, 1, '2014-06-12', '10:30:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (10, 0, 28, 1, '2014-06-13', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (11, 0, 28, 1, '2014-06-16', '09:00:00', '17:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (12, 0, 28, 1, '2014-06-17', '08:30:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (13, 0, 28, 1, '2014-06-18', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (14, 0, 28, 1, '2014-06-19', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (15, 0, 28, 1, '2014-06-20', '08:00:00', '16:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (16, 0, 28, 1, '2014-06-24', '10:00:00', '17:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (17, 0, 28, 1, '2014-06-25', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (18, 0, 28, 1, '2014-06-26', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (19, 0, 28, 1, '2014-06-27', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (20, 0, 28, 1, '2014-06-28', '11:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (21, 0, 28, 1, '2014-06-30', '12:00:00', '15:00:00'); /* WORKTIMES project 1000.2014.1 EMPLOYEE 2 */ INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (22, 0, 28, 2, '2014-06-02', '08:30:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (23, 0, 28, 2, '2014-06-03', '09:45:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (24, 0, 28, 2, '2014-06-04', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (25, 0, 28, 2, '2014-06-05', '10:15:00', '18:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (26, 0, 28, 2, '2014-06-06', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (27, 0, 28, 2, '2014-06-09', '09:00:00', '17:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (28, 0, 28, 2, '2014-06-10', '09:15:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (29, 0, 28, 2, '2014-06-11', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (30, 0, 28, 2, '2014-06-12', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (31, 0, 28, 2, '2014-06-13', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (32, 0, 28, 2, '2014-06-16', '09:00:00', '17:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (33, 0, 28, 2, '2014-06-17', '08:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (34, 0, 28, 2, '2014-06-18', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (35, 0, 28, 2, '2014-06-19', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (36, 0, 28, 2, '2014-06-20', '09:30:00', '16:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (37, 0, 28, 2, '2014-06-24', '09:00:00', '17:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (38, 0, 28, 2, '2014-06-25', '08:45:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (39, 0, 28, 2, '2014-06-26', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (40, 0, 28, 2, '2014-06-27', '09:45:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (41, 0, 28, 2, '2014-06-28', '09:00:00', '17:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (42, 0, 28, 2, '2014-06-30', '12:00:00', '15:00:00'); /* WORKTIMES project 1002.2014.1 EMPLOYEE 3 */ INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (43, 0, 32, 3, '2014-06-02', '12:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (44, 0, 32, 3, '2014-06-03', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (45, 0, 32, 3, '2014-06-04', '09:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (46, 0, 32, 3, '2014-06-05', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (47, 0, 32, 3, '2014-06-06', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (48, 0, 32, 3, '2014-06-09', '12:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (49, 0, 32, 3, '2014-06-10', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (50, 0, 32, 3, '2014-06-11', '07:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (51, 0, 32, 3, '2014-06-12', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (52, 0, 32, 3, '2014-06-13', '09:00:00', '16:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (53, 0, 32, 3, '2014-06-16', '13:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (54, 0, 32, 3, '2014-06-17', '08:00:00', '17:45:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (55, 0, 32, 3, '2014-06-18', '09:45:00', '18:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (56, 0, 32, 3, '2014-06-19', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (57, 0, 32, 3, '2014-06-20', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (58, 0, 32, 3, '2014-06-24', '11:00:00', '18:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (59, 0, 32, 3, '2014-06-25', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (60, 0, 32, 3, '2014-06-26', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (61, 0, 32, 3, '2014-06-27', '11:00:00', '19:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (62, 0, 32, 3, '2014-06-28', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (63, 0, 32, 3, '2014-06-30', '12:00:00', '17:00:00'); /* WORKTIMES project 1002.2014.1 EMPLOYEE 4 */ INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (64, 0, 32, 4, '2014-06-02', '11:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (65, 0, 32, 4, '2014-06-03', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (66, 0, 32, 4, '2014-06-04', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (67, 0, 32, 4, '2014-06-05', '10:15:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (68, 0, 32, 4, '2014-06-06', '07:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (69, 0, 32, 4, '2014-06-09', '12:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (70, 0, 32, 4, '2014-06-10', '08:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (71, 0, 32, 4, '2014-06-11', '07:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (72, 0, 32, 4, '2014-06-12', '09:00:00', '17:45:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (73, 0, 32, 4, '2014-06-13', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (74, 0, 32, 4, '2014-06-16', '13:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (75, 0, 32, 4, '2014-06-17', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (76, 0, 32, 4, '2014-06-18', '09:00:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (77, 0, 32, 4, '2014-06-19', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (78, 0, 32, 4, '2014-06-20', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (79, 0, 32, 4, '2014-06-24', '11:00:00', '18:15:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (80, 0, 32, 4, '2014-06-25', '09:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (81, 0, 32, 4, '2014-06-26', '08:30:00', '16:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (82, 0, 32, 4, '2014-06-27', '10:00:00', '17:30:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (83, 0, 32, 4, '2014-06-28', '09:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (84, 0, 32, 4, '2014-06-30', '12:00:00', '17:00:00'); /* VACATION REQUESTS */ INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (0, 0, 1, '2013-12-18', '2014-01-03', 10, 'APPROVED', '2013-12-01 11:00:00', '2013-12-02 10:30:13'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (1, 0, 1, '2014-07-10', '2014-07-18', 7, 'APPROVED', '2014-06-01 11:00:00', 2, '2014-06-03 10:00:03'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (2, 0, 1, '2014-04-07', '2014-04-11', 5, 'REJECTED', '2014-03-15 11:00:00', 5, '2014-03-19 13:05:06'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (3, 0, 2, '2013-12-19', '2014-01-03', 9, 'APPROVED', '2013-12-01 11:00:00', '2013-12-02 10:30:13'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (4, 0, 2, '2014-07-14', '2014-07-17', 4, 'APPROVED', '2014-06-01 11:00:00', 3, '2014-06-03 10:00:03'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (5, 0, 2, '2014-04-07', '2014-04-11', 5, 'REJECTED', '2014-03-15 11:00:00', 4, '2014-03-19 13:05:06'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (6, 0, 3, '2013-12-19', '2014-01-03', 9, 'APPROVED', '2013-12-01 11:00:00', '2013-12-02 10:30:13'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (7, 0, 3, '2014-07-10', '2014-07-18', 8, 'APPROVED', '2014-06-01 11:00:00', 1, '2014-06-03 10:00:03'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (8, 0, 3, '2014-04-07', '2014-04-11', 5, 'REJECTED', '2014-03-15 11:00:00', 6, '2014-03-19 13:05:06'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (9, 0, 4, '2013-12-19', '2014-01-03', 9, 'APPROVED', '2013-12-01 11:00:00', '2013-12-02 10:30:13'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (10, 0, 4, '2014-07-14', '2014-07-18', 5, 'APPROVED', '2014-06-01 11:00:00', 2, '2014-06-03 10:00:03'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (11, 0, 4, '2014-04-07', '2014-04-11', 5, 'REJECTED', '2014-03-15 11:00:00', 5, '2014-03-19 13:05:06'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (12, 0, 5, '2013-12-19', '2014-01-03', 9, 'APPROVED', '2013-12-01 11:00:00', '2013-12-02 10:30:13'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (13, 0, 5, '2014-07-14', '2014-07-15', 2, 'APPROVED', '2014-06-01 11:00:00', 1, '2014-06-03 10:00:03'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (14, 0, 5, '2014-04-07', '2014-04-11', 5, 'REJECTED', '2014-03-15 11:00:00', 1, '2014-03-19 13:05:06'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (15, 0, 6, '2013-12-19', '2014-01-03', 9, 'APPROVED', '2013-12-01 11:00:00', '2013-12-02 10:30:13'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (16, 0, 6, '2014-07-14', '2014-07-14', 1, 'APPROVED', '2014-06-01 11:00:00', 4, '2014-06-03 10:00:03'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (17, 0, 6, '2014-04-07', '2014-04-11', 5, 'REJECTED', '2014-03-15 11:00:00', 3, '2014-03-19 13:05:06'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (18, 0, 9, '2014-04-24', '2014-04-29', 5, 'APPROVED', '2014-01-03 11:00:00', '2014-01-10 04:00:00'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approvalDate) VALUES (19, 0, 9, '2014-05-05', '2014-05-05', 1, 'APPROVED', '2014-04-01 10:00:00', '2014-04-08 04:00:00'); INSERT INTO vacationRequest (id, version, employee_id, startDate, endDate, numberOfDays, status, submissionTime, approver_id, approvalDate) VALUES (20, 0, 9, '2014-06-17', '2014-06-24', 6, 'REJECTED', '2014-05-03 16:00:00', 1, '2014-05-03 16:10:01'); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (0, 0, 34, '2014-07-01', '2014-07-01', 31.34, 19, '2014-04-16 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (1, 0, 34, '2014-07-01', '2014-07-04', 350, 19, '2014-07-16 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (2, 0, 35, '2014-06-02', '2014-06-02', 32.40, 19, '2014-07-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (3, 0, 35, '2014-06-02', '2014-06-05', 350, 19, '2014-07-01 12:40:51', 'HOTEL', true); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (4, 0, 36, '2014-04-01', '2014-04-10', 30.33, 19, '2014-05-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (5, 0, 36, '2014-04-01', '2014-04-10', 340, 19, '2014-05-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (6, 0, 37, '2014-05-01', '2014-05-10', 330.13, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (7, 0, 37, '2014-05-01', '2014-05-10', 1000, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (8, 0, 38, '2014-07-01', '2014-07-01', 15.16, 19, '2014-04-16 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (9, 0, 38, '2014-07-01', '2014-07-04', 250, 19, '2014-07-16 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (10, 0, 39, '2014-06-02', '2014-06-02', 32.40, 19, '2014-07-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (11, 0, 39, '2014-06-02', '2014-06-05', 350, 19, '2014-07-01 12:40:51', 'HOTEL', true); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (12, 0, 40, '2014-04-01', '2014-04-10', 30.33, 19, '2014-05-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (13, 0, 40, '2014-04-01', '2014-04-10', 340, 19, '2014-05-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (14, 0, 41, '2014-05-01', '2014-05-10', 330.13, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (15, 0, 41, '2014-05-01', '2014-05-10', 1000, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (16, 0, 42, '2014-07-01', '2014-07-01', 131.34, 19, '2014-04-16 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (17, 0, 42, '2014-07-01', '2014-07-04', 1350, 19, '2014-07-16 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (18, 0, 43, '2014-06-02', '2014-06-02', 32.40, 19, '2014-07-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (19, 0, 43, '2014-06-02', '2014-06-05', 350, 19, '2014-07-01 12:40:51', 'HOTEL', true); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (20, 0, 44, '2014-04-01', '2014-04-10', 30.33, 19, '2014-05-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (21, 0, 44, '2014-04-01', '2014-04-10', 340, 19, '2014-05-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (22, 0, 45, '2014-05-01', '2014-05-10', 330.13, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (23, 0, 45, '2014-05-01', '2014-05-10', 1000, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (24, 0, 46, '2014-07-01', '2014-07-01', 85.34, 19, '2014-04-16 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (25, 0, 46, '2014-07-01', '2014-07-04', 250, 19, '2014-07-16 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (26, 0, 47, '2014-06-02', '2014-06-02', 32.40, 19, '2014-07-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (27, 0, 47, '2014-06-02', '2014-06-05', 350, 19, '2014-07-01 12:40:51', 'HOTEL', true); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (28, 0, 48, '2014-04-01', '2014-04-10', 30.33, 19, '2014-05-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (29, 0, 48, '2014-04-01', '2014-04-10', 340, 19, '2014-05-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (30, 0, 49, '2014-05-01', '2014-05-10', 330.13, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (31, 0, 49, '2014-05-01', '2014-05-10', 1000, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (32, 0, 50, '2014-07-01', '2014-07-01', 16.34, 19, '2014-04-16 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (33, 0, 50, '2014-07-01', '2014-07-04', 370, 19, '2014-07-16 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (34, 0, 51, '2014-06-02', '2014-06-02', 32.40, 19, '2014-07-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (35, 0, 51, '2014-06-02', '2014-06-05', 350, 19, '2014-07-01 12:40:51', 'HOTEL', true); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (36, 0, 52, '2014-04-01', '2014-04-10', 30.33, 19, '2014-05-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (37, 0, 52, '2014-04-01', '2014-04-10', 340, 19, '2014-05-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (38, 0, 53, '2014-05-01', '2014-05-10', 330.13, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (39, 0, 53, '2014-05-01', '2014-05-10', 1000, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (40, 0, 54, '2014-07-01', '2014-07-01', 30.54, 19, '2014-04-16 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (41, 0, 54, '2014-07-01', '2014-07-04', 330, 19, '2014-07-16 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (42, 0, 55, '2014-06-02', '2014-06-02', 32.40, 19, '2014-07-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (43, 0, 55, '2014-06-02', '2014-06-05', 350, 19, '2014-07-01 12:40:51', 'HOTEL', true); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (44, 0, 56, '2014-04-01', '2014-04-10', 30.33, 19, '2014-05-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (45, 0, 56, '2014-04-01', '2014-04-10', 340, 19, '2014-05-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (46, 0, 57, '2014-05-01', '2014-05-10', 330.13, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (47, 0, 57, '2014-05-01', '2014-05-10', 1000, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (48, 0, 58, '2013-11-01', '2013-11-30', 210.31, 19, '2013-12-03 07:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (49, 0, 58, '2013-11-01', '2013-11-30', 2000, 19, '2013-12-03 07:01:44', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (50, 0, 59, '2013-12-01', '2013-12-24', 190.45, 19, '2013-12-26 07:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (51, 0, 59, '2013-12-01', '2013-12-24', 2100, 19, '2013-12-26 07:01:44', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (52, 0, 60, '2014-01-04', '2014-01-31', 250.99, 19, '2014-02-01 12:40:51', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (53, 0, 60, '2014-01-04', '2014-01-31', 1900, 19, '2014-02-01 12:40:51', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (54, 0, 61, '2014-02-01', '2014-02-28', 150.56, 19, '2014-04-01 08:31:12', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (55, 0, 61, '2014-02-01', '2014-02-28', 1500, 19, '2014-04-01 08:33:12', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (56, 0, 62, '2014-05-01', '2014-05-31', 450, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (57, 0, 62, '2014-05-01', '2014-05-31', 2030, 19, '2014-06-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (58, 0, 63, '2014-06-01', '2014-06-30', 390, 19, '2014-07-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (59, 0, 63, '2014-06-01', '2014-06-30', 3000, 19, '2014-07-01 10:00:30', 'HOTEL', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (60, 0, 64, '2014-07-01', '2014-07-31', 200, 19, '2014-06-01 10:00:30', 'TAXI', false); INSERT INTO travelExpense (id, version, report_id, fromDate, toDate, cost, vat, submissionDate, type, paid) VALUES (61, 0, 64, '2014-07-01', '2014-07-31', 1500, 19, '2014-06-01 10:00:30', 'HOTEL', false); /* BILLABLE TIMES */ INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (0, 0, 1, 28, '2014-06-02', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (1, 0, 1, 28, '2014-06-03', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (2, 0, 1, 28, '2014-06-04', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (3, 0, 1, 28, '2014-06-05', 450); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (4, 0, 1, 28, '2014-06-06', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (5, 0, 1, 28, '2014-06-09', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (6, 0, 1, 28, '2014-06-10', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (7, 0, 1, 28, '2014-06-11', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (8, 0, 1, 28, '2014-06-12', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (9, 0, 1, 28, '2014-06-13', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (10, 0, 1, 28, '2014-06-16', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (11, 0, 1, 28, '2014-06-17', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (12, 0, 1, 28, '2014-06-18', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (13, 0, 1, 28, '2014-06-19', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (14, 0, 1, 28, '2014-06-20', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (15, 0, 1, 28, '2014-06-24', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (16, 0, 1, 28, '2014-06-25', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (17, 0, 1, 28, '2014-06-26', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (18, 0, 1, 28, '2014-06-27', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (19, 0, 1, 28, '2014-06-28', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (20, 0, 1, 28, '2014-06-30', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (21, 0, 2, 28, '2014-06-02', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (22, 0, 2, 28, '2014-06-03', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (23, 0, 2, 28, '2014-06-04', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (24, 0, 2, 28, '2014-06-05', 450); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (25, 0, 2, 28, '2014-06-06', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (26, 0, 2, 28, '2014-06-09', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (27, 0, 2, 28, '2014-06-10', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (28, 0, 2, 28, '2014-06-11', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (29, 0, 2, 28, '2014-06-12', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (30, 0, 2, 28, '2014-06-13', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (31, 0, 2, 28, '2014-06-16', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (32, 0, 2, 28, '2014-06-17', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (33, 0, 2, 28, '2014-06-18', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (34, 0, 2, 28, '2014-06-19', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (35, 0, 2, 28, '2014-06-20', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (36, 0, 2, 28, '2014-06-24', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (37, 0, 2, 28, '2014-06-25', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (38, 0, 2, 28, '2014-06-26', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (39, 0, 2, 28, '2014-06-27', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (40, 0, 2, 28, '2014-06-28', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (41, 0, 2, 28, '2014-06-30', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (42, 0, 3, 32, '2014-06-02', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (43, 0, 3, 32, '2014-06-03', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (44, 0, 3, 32, '2014-06-04', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (45, 0, 3, 32, '2014-06-05', 450); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (46, 0, 3, 32, '2014-06-06', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (47, 0, 3, 32, '2014-06-09', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (48, 0, 3, 32, '2014-06-10', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (49, 0, 3, 32, '2014-06-11', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (50, 0, 3, 32, '2014-06-12', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (51, 0, 3, 32, '2014-06-13', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (52, 0, 3, 32, '2014-06-16', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (53, 0, 3, 32, '2014-06-17', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (54, 0, 3, 32, '2014-06-18', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (55, 0, 3, 32, '2014-06-19', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (56, 0, 3, 32, '2014-06-20', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (57, 0, 3, 32, '2014-06-24', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (58, 0, 3, 32, '2014-06-25', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (59, 0, 3, 32, '2014-06-26', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (60, 0, 3, 32, '2014-06-27', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (61, 0, 3, 32, '2014-06-28', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (62, 0, 3, 32, '2014-06-30', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (63, 0, 4, 32, '2014-06-02', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (64, 0, 4, 32, '2014-06-03', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (65, 0, 4, 32, '2014-06-04', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (66, 0, 4, 32, '2014-06-05', 450); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (67, 0, 4, 32, '2014-06-06', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (68, 0, 4, 32, '2014-06-09', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (69, 0, 4, 32, '2014-06-10', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (70, 0, 4, 32, '2014-06-11', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (71, 0, 4, 32, '2014-06-12', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (72, 0, 4, 32, '2014-06-13', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (73, 0, 4, 32, '2014-06-16', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (74, 0, 4, 32, '2014-06-17', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (75, 0, 4, 32, '2014-06-18', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (76, 0, 4, 32, '2014-06-19', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (77, 0, 4, 32, '2014-06-20', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (78, 0, 4, 32, '2014-06-24', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (79, 0, 4, 32, '2014-06-25', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (80, 0, 4, 32, '2014-06-26', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (81, 0, 4, 32, '2014-06-27', 480); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (82, 0, 4, 32, '2014-06-28', 360); INSERT INTO billableTime (id, version, employee, project, date, minutes) VALUES (83, 0, 4, 32, '2014-06-30', 480); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (0, 0, '1003.2014.1-2014.01-1', 22, '2014-01-04', 1500.00, 'PAID', '2014-02-01'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (1, 0, '1003.2014.1-2014.01-2', 22, '2014-01-04', 2300.00, 'PAID', '2014-02-01'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (2, 0, '1003.2014.1-2014.01-3', 22, '2014-01-04', 6000.00, 'PAID', '2014-02-01'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (3, 0, '1003.2014.1-2014.02-1', 22, '2014-02-15', 3000.00, 'PAID', '2014-03-20'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (4, 0, '1003.2014.1-2014.02-2', 22, '2014-02-15', 4020.00, 'PAID', '2014-03-20'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (5, 0, '1000.2014.1-2014.01-1', 19, '2014-01-15', 12570.00, 'PAID', '2014-03-01'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (6, 0, '1000.2014.1-2014.06-1', 20, '2014-06-03', 7400.00, 'PAID', '2014-07-01'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (7, 0, '1000.2014.1-2014.06-2', 20, '2014-06-03', 7600.00, 'PAID', '2014-07-01'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (8, 0, '1002.2014.1-2014.06-1', 21, '2014-06-01', 13000.00, 'PAID', '2014-06-15'); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (9, 0, '1002.2014.1-2014.06-2', 21, '2014-06-01', 25100.00, 'PAID', '2014-07-15'); ================================================ FILE: src/main/resources/db/migration/V10__modify_travel_expense_add_comment.sql ================================================ alter table TravelExpense add column comment varchar(255); ================================================ FILE: src/main/resources/db/migration/V11__add_travel_expense_report_comments.sql ================================================ create table TravelExpenseReportComment ( id int8 not null, submissionDate timestamp, text varchar(255), employee_id int8, travelExpenseReport_id int8, primary key (id) ); alter table TravelExpenseReportComment add constraint FKCEF7236D55198E9B foreign key (employee_id) references Employee; alter table TravelExpenseReportComment add constraint FKCEF7236D6F1C4208 foreign key (travelExpenseReport_id) references TravelExpenseReport; ================================================ FILE: src/main/resources/db/migration/V12__modify_company_add_time_for_payment.sql ================================================ alter table Company add column timeForPayment int4; ================================================ FILE: src/main/resources/db/migration/V13__modify_travel_expense_reports_add_debitor_and_project.sql ================================================ alter table TravelExpenseReport add column debitor_id int8 not null default 0; update TravelExpenseReport set debitor_id = ( select id from Company where companyId = '1000' ); alter table TravelExpenseReport alter column debitor_id drop default; alter table TravelExpenseReport add column project_id int8; alter table TravelExpenseReport add constraint FK854DBA92D4C285A6 foreign key (project_id) references Project; alter table TravelExpenseReport add constraint FK854DBA92F02B33F8 foreign key (debitor_id) references Company; ================================================ FILE: src/main/resources/db/migration/V14__add_uuid_mapping.sql ================================================ CREATE TABLE uuid_mapping ( id int8 not null, uuid varchar(40) ); ================================================ FILE: src/main/resources/db/migration/V15__migrate_credentials.sql ================================================ ALTER TABLE Employee ADD COLUMN email VARCHAR(255); ALTER TABLE Employee ADD UNIQUE (email); -- transfer the email to the employee UPDATE Employee e SET email = (SELECT email FROM Credential WHERE id = e.id); ALTER TABLE Employee ALTER COLUMN email SET NOT NULL; CREATE TABLE Settings ( id INT8 NOT NULL, type VARCHAR(255), value VARCHAR(255) NOT NULL, employee_id INT8 NOT NULL, PRIMARY KEY (id) ); alter table Settings add constraint FK_18fk82qvt48n13545pq4dt2fr foreign key (employee_id) references Employee; -- transfer locale setting from credential to settins INSERT INTO Settings (id, type, value, employee_id) ( SELECT nextval('hibernate_sequence'), 'LOCALE', locale, id FROM Credential ); DROP TABLE credential_authority; DROP TABLE credential; DROP TABLE authority; ================================================ FILE: src/main/resources/db/migration/V16__add_holidays_2015.sql ================================================ insert into holiday (id, day, name, federalstate) values (167, '2014-12-24', 'Weihnachten', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (168, '2014-12-24', 'Weihnachten', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (169, '2014-12-24', 'Weihnachten', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (170, '2014-12-24', 'Weihnachten', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (171, '2014-12-24', 'Weihnachten', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (172, '2014-12-24', 'Weihnachten', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (173, '2014-12-24', 'Weihnachten', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (174, '2014-12-24', 'Weihnachten', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (175, '2014-12-24', 'Weihnachten', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (176, '2014-12-24', 'Weihnachten', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (177, '2014-12-24', 'Weihnachten', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (178, '2014-12-24', 'Weihnachten', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (179, '2014-12-24', 'Weihnachten', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (180, '2014-12-24', 'Weihnachten', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (181, '2014-12-24', 'Weihnachten', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (182, '2014-12-24', 'Weihnachten', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (183, '2014-12-31', 'Silvester', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (184, '2014-12-31', 'Silvester', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (185, '2014-12-31', 'Silvester', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (186, '2014-12-31', 'Silvester', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (187, '2014-12-31', 'Silvester', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (188, '2014-12-31', 'Silvester', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (189, '2014-12-31', 'Silvester', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (190, '2014-12-31', 'Silvester', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (191, '2014-12-31', 'Silvester', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (192, '2014-12-31', 'Silvester', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (193, '2014-12-31', 'Silvester', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (194, '2014-12-31', 'Silvester', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (195, '2014-12-31', 'Silvester', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (196, '2014-12-31', 'Silvester', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (197, '2014-12-31', 'Silvester', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (198, '2014-12-31', 'Silvester', 'THUERINGEN'); ================================================ FILE: src/main/resources/db/migration/V17__add_holidays_2015.sql ================================================ -- 2015 - Baden Württemberg insert into holiday (id, day, name, federalstate) values (199, '2015-01-01', 'Neujahr', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (200, '2015-01-06', 'Heilige Drei Könige', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (201, '2015-04-03', 'Karfreitag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (202, '2015-04-06', 'Ostermontag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (203, '2015-05-01', 'Tag der Arbeit', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (204, '2015-05-14', 'Christi Himmelfahrt', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (205, '2015-05-25', 'Pfingstmontag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (206, '2015-06-04', 'Fronleichnam', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (207, '2015-10-03', 'Tag der Deutschen Einheit', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (208, '2015-11-01', 'Allerheiligen', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (209, '2015-12-24', 'Weihnachten', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (210, '2015-12-25', '1. Weihnachtstag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (211, '2015-12-26', '2. Weihnachtstag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (212, '2015-12-31', 'Silvester', 'BADEN_WUERTTEMBERG'); -- 2015 - Bayern insert into holiday (id, day, name, federalstate) values (213, '2015-01-01', 'Neujahr', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (214, '2015-01-06', 'Heilige Drei Könige', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (215, '2015-04-03', 'Karfreitag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (216, '2015-04-06', 'Ostermontag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (217, '2015-05-01', 'Tag der Arbeit', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (218, '2015-05-14', 'Christi Himmelfahrt', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (219, '2015-05-25', 'Pfingstmontag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (220, '2015-06-04', 'Fronleichnam', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (221, '2015-08-15', 'Mariä Himmelfahrt', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (222, '2015-10-03', 'Tag der Deutschen Einheit', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (223, '2015-11-01', 'Allerheiligen', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (224, '2015-12-24', 'Weihnachten', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (225, '2015-12-25', '1. Weihnachtstag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (226, '2015-12-26', '2. Weihnachtstag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (227, '2015-12-31', 'Silvester', 'BAYERN'); -- 2015 - Berlin insert into holiday (id, day, name, federalstate) values (228, '2015-01-01', 'Neujahr', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (229, '2015-04-03', 'Karfreitag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (230, '2015-04-06', 'Ostermontag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (231, '2015-05-01', 'Tag der Arbeit', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (232, '2015-05-14', 'Christi Himmelfahrt', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (233, '2015-05-25', 'Pfingstmontag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (234, '2015-10-03', 'Tag der Deutschen Einheit', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (235, '2015-12-24', 'Weihnachten', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (236, '2015-12-25', '1. Weihnachtstag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (237, '2015-12-26', '2. Weihnachtstag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (238, '2015-12-31', 'Silvester', 'BERLIN'); -- 2015 - Brandenburg insert into holiday (id, day, name, federalstate) values (239, '2015-01-01', 'Neujahr', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (240, '2015-04-03', 'Karfreitag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (241, '2015-04-06', 'Ostermontag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (242, '2015-05-01', 'Tag der Arbeit', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (243, '2015-05-14', 'Christi Himmelfahrt', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (244, '2015-05-25', 'Pfingstmontag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (245, '2015-10-03', 'Tag der Deutschen Einheit', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (246, '2015-10-31', 'Reformationstag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (247, '2015-12-24', 'Weihnachten', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (248, '2015-12-25', '1. Weihnachtstag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (249, '2015-12-26', '2. Weihnachtstag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (250, '2015-12-31', 'Silvester', 'BRANDENBURG'); -- 2015 - Bremen insert into holiday (id, day, name, federalstate) values (251, '2015-01-01', 'Neujahr', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (252, '2015-04-03', 'Karfreitag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (253, '2015-04-06', 'Ostermontag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (254, '2015-05-01', 'Tag der Arbeit', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (255, '2015-05-14', 'Christi Himmelfahrt', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (256, '2015-05-25', 'Pfingstmontag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (257, '2015-10-03', 'Tag der Deutschen Einheit', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (258, '2015-12-24', 'Weihnachten', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (259, '2015-12-25', '1. Weihnachtstag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (260, '2015-12-26', '2. Weihnachtstag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (261, '2015-12-31', 'Silvester', 'BREMEN'); -- 2015 - Hamburg insert into holiday (id, day, name, federalstate) values (262, '2015-01-01', 'Neujahr', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (263, '2015-04-03', 'Karfreitag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (264, '2015-04-06', 'Ostermontag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (265, '2015-05-01', 'Tag der Arbeit', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (266, '2015-05-14', 'Christi Himmelfahrt', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (267, '2015-05-25', 'Pfingstmontag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (268, '2015-10-03', 'Tag der Deutschen Einheit', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (269, '2015-12-24', 'Weihnachten', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (270, '2015-12-25', '1. Weihnachtstag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (271, '2015-12-26', '2. Weihnachtstag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (272, '2015-12-31', 'Silvester', 'HAMBURG'); -- 2015 - Hessen insert into holiday (id, day, name, federalstate) values (273, '2015-01-01', 'Neujahr', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (274, '2015-04-03', 'Karfreitag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (275, '2015-04-06', 'Ostermontag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (276, '2015-05-01', 'Tag der Arbeit', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (277, '2015-05-14', 'Christi Himmelfahrt', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (278, '2015-05-25', 'Pfingstmontag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (279, '2015-06-04', 'Fronleichnam', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (280, '2015-10-03', 'Tag der Deutschen Einheit', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (281, '2015-12-24', 'Weihnachten', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (282, '2015-12-25', '1. Weihnachtstag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (283, '2015-12-26', '2. Weihnachtstag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (284, '2015-12-31', 'Silvester', 'HESSEN'); -- 2015 - Mecklenburg-Vorpommern insert into holiday (id, day, name, federalstate) values (285, '2015-01-01', 'Neujahr', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (286, '2015-04-03', 'Karfreitag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (287, '2015-04-06', 'Ostermontag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (288, '2015-05-01', 'Tag der Arbeit', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (289, '2015-05-14', 'Christi Himmelfahrt', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (290, '2015-05-25', 'Pfingstmontag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (291, '2015-10-03', 'Tag der Deutschen Einheit', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (292, '2015-10-31', 'Reformationstag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (293, '2015-12-24', 'Weihnachten', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (294, '2015-12-25', '1. Weihnachtstag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (295, '2015-12-26', '2. Weihnachtstag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (296, '2015-12-31', 'Silvester', 'MECKLENBURG_VORPOMMERN'); -- 2015 - Niedersachsen insert into holiday (id, day, name, federalstate) values (297, '2015-01-01', 'Neujahr', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (298, '2015-04-03', 'Karfreitag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (299, '2015-04-06', 'Ostermontag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (300, '2015-05-01', 'Tag der Arbeit', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (301, '2015-05-14', 'Christi Himmelfahrt', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (302, '2015-05-25', 'Pfingstmontag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (303, '2015-10-03', 'Tag der Deutschen Einheit', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (304, '2015-12-24', 'Weihnachten', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (305, '2015-12-25', '1. Weihnachtstag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (306, '2015-12-26', '2. Weihnachtstag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (307, '2015-12-31', 'Silvester', 'NIEDERSACHSEN'); -- 2015 - Nordrhein-Westfalen insert into holiday (id, day, name, federalstate) values (308, '2015-01-01', 'Neujahr', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (309, '2015-04-03', 'Karfreitag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (310, '2015-04-06', 'Ostermontag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (311, '2015-05-01', 'Tag der Arbeit', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (312, '2015-05-14', 'Christi Himmelfahrt', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (313, '2015-05-25', 'Pfingstmontag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (314, '2015-06-04', 'Fronleichnam', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (315, '2015-10-03', 'Tag der Deutschen Einheit', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (316, '2015-11-01', 'Allerheiligen', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (317, '2015-12-24', 'Weihnachten', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (318, '2015-12-25', '1. Weihnachtstag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (319, '2015-12-26', '2. Weihnachtstag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (320, '2015-12-31', 'Silvester', 'NORDRHEIN_WESTFALEN'); -- 2015 - Rheinland-Pfalz insert into holiday (id, day, name, federalstate) values (321, '2015-01-01', 'Neujahr', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (322, '2015-04-03', 'Karfreitag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (323, '2015-04-06', 'Ostermontag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (324, '2015-05-01', 'Tag der Arbeit', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (325, '2015-05-14', 'Christi Himmelfahrt', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (326, '2015-05-25', 'Pfingstmontag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (327, '2015-06-04', 'Fronleichnam', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (328, '2015-10-03', 'Tag der Deutschen Einheit', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (329, '2015-11-01', 'Allerheiligen', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (330, '2015-12-24', 'Weihnachten', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (331, '2015-12-25', '1. Weihnachtstag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (332, '2015-12-26', '2. Weihnachtstag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (333, '2015-12-31', 'Silvester', 'RHEINLAND_PFALZ'); -- 2015 - Saarland insert into holiday (id, day, name, federalstate) values (334, '2015-01-01', 'Neujahr', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (335, '2015-04-03', 'Karfreitag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (336, '2015-04-06', 'Ostermontag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (337, '2015-05-01', 'Tag der Arbeit', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (338, '2015-05-14', 'Christi Himmelfahrt', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (339, '2015-05-25', 'Pfingstmontag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (340, '2015-06-04', 'Fronleichnam', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (341, '2015-08-15', 'Mariä Himmelfahrt', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (342, '2015-10-03', 'Tag der Deutschen Einheit', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (343, '2015-11-01', 'Allerheiligen', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (344, '2015-12-24', 'Weihnachten', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (345, '2015-12-25', '1. Weihnachtstag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (346, '2015-12-26', '2. Weihnachtstag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (347, '2015-12-31', 'Silvester', 'SAARLAND'); -- 2015 - Sachsen insert into holiday (id, day, name, federalstate) values (348, '2015-01-01', 'Neujahr', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (349, '2015-04-03', 'Karfreitag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (350, '2015-04-06', 'Ostermontag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (351, '2015-05-01', 'Tag der Arbeit', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (352, '2015-05-14', 'Christi Himmelfahrt', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (353, '2015-05-25', 'Pfingstmontag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (354, '2015-10-03', 'Tag der Deutschen Einheit', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (355, '2015-10-31', 'Reformationstag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (356, '2015-11-18', 'Buß- und Bettag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (357, '2015-12-24', 'Weihnachten', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (358, '2015-12-25', '1. Weihnachtstag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (359, '2015-12-26', '2. Weihnachtstag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (360, '2015-12-31', 'Silvester', 'SACHSEN'); -- 2015 - Sachsen-Anhalt insert into holiday (id, day, name, federalstate) values (361, '2015-01-01', 'Neujahr', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (362, '2015-01-06', 'Heilige Drei Könige', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (363, '2015-04-03', 'Karfreitag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (364, '2015-04-06', 'Ostermontag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (365, '2015-05-01', 'Tag der Arbeit', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (366, '2015-05-14', 'Christi Himmelfahrt', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (367, '2015-05-25', 'Pfingstmontag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (368, '2015-10-03', 'Tag der Deutschen Einheit', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (369, '2015-10-31', 'Reformationstag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (370, '2015-12-24', 'Weihnachten', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (371, '2015-12-25', '1. Weihnachtstag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (372, '2015-12-26', '2. Weihnachtstag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (373, '2015-12-31', 'Silvester', 'SACHSEN_ANHALT'); -- 2015 - Schleswig-Holstein insert into holiday (id, day, name, federalstate) values (374, '2015-01-01', 'Neujahr', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (375, '2015-04-03', 'Karfreitag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (376, '2015-04-06', 'Ostermontag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (377, '2015-05-01', 'Tag der Arbeit', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (378, '2015-05-14', 'Christi Himmelfahrt', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (379, '2015-05-25', 'Pfingstmontag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (380, '2015-10-03', 'Tag der Deutschen Einheit', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (381, '2015-12-24', 'Weihnachten', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (382, '2015-12-25', '1. Weihnachtstag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (383, '2015-12-26', '2. Weihnachtstag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (384, '2015-12-31', 'Silvester', 'SCHLESWIG_HOLSTEIN'); -- 2015 - Thüringen insert into holiday (id, day, name, federalstate) values (385, '2015-01-01', 'Neujahr', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (386, '2015-04-03', 'Karfreitag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (387, '2015-04-06', 'Ostermontag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (388, '2015-05-01', 'Tag der Arbeit', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (389, '2015-05-14', 'Christi Himmelfahrt', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (390, '2015-05-25', 'Pfingstmontag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (391, '2015-10-03', 'Tag der Deutschen Einheit', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (392, '2015-10-31', 'Tag der Deutschen Einheit', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (393, '2015-12-24', 'Weihnachten', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (394, '2015-12-25', 'Reformationstag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (395, '2015-12-26', '2. Weihnachtstag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (396, '2015-12-31', 'Silvester', 'THUERINGEN'); -- 2016 - new years eve insert into holiday (id, day, name, federalstate) values (397, '2016-01-01', 'Neujahr', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (398, '2016-01-01', 'Neujahr', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (399, '2016-01-01', 'Neujahr', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (400, '2016-01-01', 'Neujahr', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (401, '2016-01-01', 'Neujahr', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (402, '2016-01-01', 'Neujahr', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (403, '2016-01-01', 'Neujahr', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (404, '2016-01-01', 'Neujahr', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (405, '2016-01-01', 'Neujahr', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (406, '2016-01-01', 'Neujahr', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (407, '2016-01-01', 'Neujahr', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (408, '2016-01-01', 'Neujahr', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (409, '2016-01-01', 'Neujahr', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (410, '2016-01-01', 'Neujahr', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (411, '2016-01-01', 'Neujahr', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (412, '2016-01-01', 'Neujahr', 'THUERINGEN'); -- 2016 - three kings insert into holiday (id, day, name, federalstate) values (413, '2016-01-06', 'Heilige Drei Könige', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (414, '2016-01-06', 'Heilige Drei Könige', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (415, '2016-01-06', 'Heilige Drei Könige', 'SACHSEN_ANHALT'); ================================================ FILE: src/main/resources/db/migration/V18__expense_paid_marker.sql ================================================ ALTER TABLE travelexpense ADD COLUMN paid BOOLEAN DEFAULT false; ================================================ FILE: src/main/resources/db/migration/V19__add_employee_address.sql ================================================ ALTER TABLE employee ADD COLUMN address_id int8; ALTER TABLE employee ADD CONSTRAINT employee_contact_address FOREIGN KEY (address_id) REFERENCES address; ================================================ FILE: src/main/resources/db/migration/V1__create_schema.sql ================================================ create table Address ( id int8 not null, city varchar(255), country varchar(255), houseNumber varchar(255), street varchar(255), version int4, zipCode varchar(255), primary key (id) ); create table Authority ( id int8 not null, authority varchar(255) unique, authorityOrder int4, primary key (id) ); create table BillableTime ( id int8 not null, date date, minutes int4, version int4, employee int8, project int8, primary key (id), unique (employee, project, date) ); create table Company ( id int8 not null, companyId int8 unique, name varchar(255), version int4, address_id int8, primary key (id) ); create table ContactPerson ( id int8 not null, email varchar(255), firstName varchar(255), lastName varchar(255), phone varchar(255), salutation varchar(255), version int4, company int8, primary key (id) ); create table Credential ( id int8 not null, email varchar(255) unique, enabled boolean, locale varchar(255), primary key (id) ); create table Credential_Authority ( Credential_id int8 not null, authorities_id int8 not null ); create table Employee ( id int8 not null, federalState varchar(255), firstName varchar(255), hourlyCostRate numeric(19, 2), joinDate date, lastName varchar(255), leaveDate date, phoneNumber varchar(255), salary numeric(19, 2), title varchar(255), vacationEntitlement float4, version int4, primary key (id) ); create table Holiday ( id int8 not null, day date, federalState varchar(255), name varchar(255), primary key (id) ); create table Project ( id int8 not null, dailyRate numeric(19, 2), fixedPrice numeric(19, 2), hourlyRate numeric(19, 2), identifier varchar(255) unique, name varchar(255), version int4, volume int4, company_id int8, debitor_id int8, primary key (id) ); create table VacationRequest ( id int8 not null, approvalDate date, endDate date, numberOfDays int4, startDate date, status varchar(255), submissionTime timestamp, version int4, approver_id int8, employee_id int8, primary key (id) ); create table WorkTime ( id int8 not null, comment varchar(255), date date, endTime time, startTime time, version int4, employee int8, project int8, primary key (id) ); create table TravelExpense ( id int8 not null, cost numeric(19, 2), fromDate date, submissionDate timestamp, toDate date, type varchar(255), vat numeric(19, 2), version int4, report_id int8, primary key (id) ); create table TravelExpenseReport ( id int8 not null, status varchar(255), version int4, employee_id int8, primary key (id) ); alter table BillableTime add constraint FK3EBA06E37BE2CBE foreign key (project) references Project; alter table BillableTime add constraint FK3EBA06E65C090FD foreign key (employee) references Employee; alter table Company add constraint FK9BDFD45D9475612A foreign key (address_id) references Address; alter table ContactPerson add constraint FK4E7B4375FA3D5CEA foreign key (company) references Company; alter table Credential_Authority add constraint FK3DA6FD5B34EFD736 foreign key (authorities_id) references Authority; alter table Credential_Authority add constraint FK3DA6FD5B27F2CCA0 foreign key (Credential_id) references Credential; alter table Project add constraint FK50C8E2F98F0FA88A foreign key (company_id) references Company; alter table Project add constraint FK50C8E2F9F02B33F8 foreign key (debitor_id) references Company; alter table VacationRequest add constraint FK266F9CD248918324 foreign key (approver_id) references Employee; alter table VacationRequest add constraint FK266F9CD255198E9B foreign key (employee_id) references Employee; alter table WorkTime add constraint FK5EE019E37BE2CBE foreign key (project) references Project; alter table WorkTime add constraint FK5EE019E65C090FD foreign key (employee) references Employee; alter table TravelExpense add constraint FK1BC7BFBE1E281C46 foreign key (report_id) references TravelExpenseReport; alter table TravelExpenseReport add constraint FK854DBA9255198E9B foreign key (employee_id) references Employee; create sequence hibernate_sequence; ================================================ FILE: src/main/resources/db/migration/V20__add_employe_deleted.sql ================================================ ALTER TABLE employee ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE; ================================================ FILE: src/main/resources/db/migration/V2__add_roles.sql ================================================ insert into employee (id, version, firstName, lastName, title, hourlyCostRate, salary, federalState) values (0, 0, 'admin', 'admin', '', 0, 0, 'BERLIN'); insert into credential (id, email, enabled, locale) values (0, 'admin@techdev.de', true, 'en'); insert into authority (id, authority, authorityOrder) values (0, 'ROLE_ADMIN', 0); insert into authority (id, authority, authorityOrder) values (1, 'ROLE_SUPERVISOR', 1); insert into authority (id, authority, authorityOrder) values (2, 'ROLE_EMPLOYEE', 2); insert into credential_authority (credential_id, authorities_id) values (0, 0); ================================================ FILE: src/main/resources/db/migration/V3__add_holidays_2014.sql ================================================ -- CLean-up delete from holiday; -- 2014 - Baden Württemberg insert into holiday (id, day, name, federalstate) values (1, '2014-01-01', 'Neujahr', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (2, '2014-01-06', 'Heilige Drei Könige', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (3, '2014-04-18', 'Karfreitag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (4, '2014-04-21', 'Ostermontag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (5, '2014-05-01', 'Tag der Arbeit', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (6, '2014-05-29', 'Christi Himmelfahrt', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (7, '2014-06-09', 'Pfingstmontag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (8, '2014-06-19', 'Fronleichnam', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (9, '2014-10-03', 'Tag der Deutschen Einheit', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (10, '2014-11-01', 'Allerheiligen', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (11, '2014-12-25', '1. Weihnachtstag', 'BADEN_WUERTTEMBERG'); insert into holiday (id, day, name, federalstate) values (12, '2014-12-26', '2. Weihnachtstag', 'BADEN_WUERTTEMBERG'); -- 2014 - Bayern insert into holiday (id, day, name, federalstate) values (13, '2014-01-01', 'Neujahr', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (14, '2014-01-06', 'Heilige Drei Könige', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (15, '2014-04-18', 'Karfreitag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (16, '2014-04-21', 'Ostermontag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (17, '2014-05-01', 'Tag der Arbeit', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (18, '2014-05-29', 'Christi Himmelfahrt', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (19, '2014-06-09', 'Pfingstmontag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (20, '2014-06-19', 'Fronleichnam', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (21, '2014-08-15', 'Mariä Himmelfahrt', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (22, '2014-10-03', 'Tag der Deutschen Einheit', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (23, '2014-11-01', 'Allerheiligen', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (24, '2014-12-25', '1. Weihnachtstag', 'BAYERN'); insert into holiday (id, day, name, federalstate) values (25, '2014-12-26', '2. Weihnachtstag', 'BAYERN'); -- 2014 - Berlin insert into holiday (id, day, name, federalstate) values (26, '2014-01-01', 'Neujahr', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (27, '2014-04-18', 'Karfreitag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (28, '2014-04-21', 'Ostermontag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (29, '2014-05-01', 'Tag der Arbeit', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (30, '2014-05-29', 'Christi Himmelfahrt', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (31, '2014-06-09', 'Pfingstmontag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (32, '2014-10-03', 'Tag der Deutschen Einheit', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (33, '2014-12-25', '1. Weihnachtstag', 'BERLIN'); insert into holiday (id, day, name, federalstate) values (34, '2014-12-26', '2. Weihnachtstag', 'BERLIN'); -- 2014 - Brandenburg insert into holiday (id, day, name, federalstate) values (35, '2014-01-01', 'Neujahr', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (36, '2014-04-18', 'Karfreitag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (37, '2014-04-21', 'Ostermontag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (38, '2014-05-01', 'Tag der Arbeit', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (39, '2014-05-29', 'Christi Himmelfahrt', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (40, '2014-06-09', 'Pfingstmontag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (41, '2014-10-03', 'Tag der Deutschen Einheit', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (42, '2014-10-31', 'Reformationstag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (43, '2014-12-25', '1. Weihnachtstag', 'BRANDENBURG'); insert into holiday (id, day, name, federalstate) values (44, '2014-12-26', '2. Weihnachtstag', 'BRANDENBURG'); -- 2014 - Bremen insert into holiday (id, day, name, federalstate) values (45, '2014-01-01', 'Neujahr', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (46, '2014-04-18', 'Karfreitag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (47, '2014-04-21', 'Ostermontag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (48, '2014-05-01', 'Tag der Arbeit', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (49, '2014-05-29', 'Christi Himmelfahrt', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (50, '2014-06-09', 'Pfingstmontag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (51, '2014-10-03', 'Tag der Deutschen Einheit', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (52, '2014-12-25', '1. Weihnachtstag', 'BREMEN'); insert into holiday (id, day, name, federalstate) values (53, '2014-12-26', '2. Weihnachtstag', 'BREMEN'); -- 2014 - Hamburg insert into holiday (id, day, name, federalstate) values (54, '2014-01-01', 'Neujahr', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (55, '2014-04-18', 'Karfreitag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (56, '2014-04-21', 'Ostermontag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (57, '2014-05-01', 'Tag der Arbeit', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (58, '2014-05-29', 'Christi Himmelfahrt', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (59, '2014-06-09', 'Pfingstmontag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (60, '2014-10-03', 'Tag der Deutschen Einheit', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (61, '2014-12-25', '1. Weihnachtstag', 'HAMBURG'); insert into holiday (id, day, name, federalstate) values (62, '2014-12-26', '2. Weihnachtstag', 'HAMBURG'); -- 2014 - Hessen insert into holiday (id, day, name, federalstate) values (63, '2014-01-01', 'Neujahr', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (64, '2014-04-18', 'Karfreitag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (65, '2014-04-21', 'Ostermontag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (66, '2014-05-01', 'Tag der Arbeit', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (67, '2014-05-29', 'Christi Himmelfahrt', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (68, '2014-06-09', 'Pfingstmontag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (69, '2014-06-19', 'Fronleichnam', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (70, '2014-10-03', 'Tag der Deutschen Einheit', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (71, '2014-12-25', '1. Weihnachtstag', 'HESSEN'); insert into holiday (id, day, name, federalstate) values (72, '2014-12-26', '2. Weihnachtstag', 'HESSEN'); -- 2014 - Mecklenburg-Vorpommern insert into holiday (id, day, name, federalstate) values (73, '2014-01-01', 'Neujahr', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (74, '2014-04-18', 'Karfreitag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (75, '2014-04-21', 'Ostermontag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (76, '2014-05-01', 'Tag der Arbeit', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (77, '2014-05-29', 'Christi Himmelfahrt', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (78, '2014-06-09', 'Pfingstmontag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (79, '2014-10-03', 'Tag der Deutschen Einheit', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (80, '2014-10-31', 'Reformationstag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (81, '2014-12-25', '1. Weihnachtstag', 'MECKLENBURG_VORPOMMERN'); insert into holiday (id, day, name, federalstate) values (82, '2014-12-26', '2. Weihnachtstag', 'MECKLENBURG_VORPOMMERN'); -- 2014 - Niedersachsen insert into holiday (id, day, name, federalstate) values (83, '2014-01-01', 'Neujahr', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (84, '2014-04-18', 'Karfreitag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (85, '2014-04-21', 'Ostermontag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (86, '2014-05-01', 'Tag der Arbeit', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (87, '2014-05-29', 'Christi Himmelfahrt', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (88, '2014-06-09', 'Pfingstmontag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (89, '2014-10-03', 'Tag der Deutschen Einheit', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (90, '2014-12-25', '1. Weihnachtstag', 'NIEDERSACHSEN'); insert into holiday (id, day, name, federalstate) values (91, '2014-12-26', '2. Weihnachtstag', 'NIEDERSACHSEN'); -- 2014 - Nordrhein-Westfalen insert into holiday (id, day, name, federalstate) values (92, '2014-01-01', 'Neujahr', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (93, '2014-04-18', 'Karfreitag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (94, '2014-04-21', 'Ostermontag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (95, '2014-05-01', 'Tag der Arbeit', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (96, '2014-05-29', 'Christi Himmelfahrt', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (97, '2014-06-09', 'Pfingstmontag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (98, '2014-06-19', 'Fronleichnam', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (99, '2014-10-03', 'Tag der Deutschen Einheit', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (100, '2014-11-01', 'Allerheiligen', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (101, '2014-12-25', '1. Weihnachtstag', 'NORDRHEIN_WESTFALEN'); insert into holiday (id, day, name, federalstate) values (102, '2014-12-26', '2. Weihnachtstag', 'NORDRHEIN_WESTFALEN'); -- 2014 - Rheinland-Pfalz insert into holiday (id, day, name, federalstate) values (103, '2014-01-01', 'Neujahr', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (104, '2014-04-18', 'Karfreitag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (105, '2014-04-21', 'Ostermontag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (106, '2014-05-01', 'Tag der Arbeit', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (107, '2014-05-29', 'Christi Himmelfahrt', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (108, '2014-06-09', 'Pfingstmontag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (109, '2014-06-19', 'Fronleichnam', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (110, '2014-10-03', 'Tag der Deutschen Einheit', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (111, '2014-11-01', 'Allerheiligen', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (112, '2014-12-25', '1. Weihnachtstag', 'RHEINLAND_PFALZ'); insert into holiday (id, day, name, federalstate) values (113, '2014-12-26', '2. Weihnachtstag', 'RHEINLAND_PFALZ'); -- 2014 - Saarland insert into holiday (id, day, name, federalstate) values (114, '2014-01-01', 'Neujahr', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (115, '2014-04-18', 'Karfreitag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (116, '2014-04-21', 'Ostermontag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (117, '2014-05-01', 'Tag der Arbeit', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (118, '2014-05-29', 'Christi Himmelfahrt', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (119, '2014-06-09', 'Pfingstmontag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (120, '2014-06-19', 'Fronleichnam', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (121, '2014-08-15', 'Mariä Himmelfahrt', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (122, '2014-10-03', 'Tag der Deutschen Einheit', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (123, '2014-11-01', 'Allerheiligen', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (124, '2014-12-25', '1. Weihnachtstag', 'SAARLAND'); insert into holiday (id, day, name, federalstate) values (125, '2014-12-26', '2. Weihnachtstag', 'SAARLAND'); -- 2014 - Sachsen insert into holiday (id, day, name, federalstate) values (126, '2014-01-01', 'Neujahr', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (127, '2014-04-18', 'Karfreitag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (128, '2014-04-21', 'Ostermontag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (129, '2014-05-01', 'Tag der Arbeit', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (130, '2014-05-29', 'Christi Himmelfahrt', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (131, '2014-06-09', 'Pfingstmontag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (132, '2014-10-03', 'Tag der Deutschen Einheit', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (133, '2014-10-31', 'Reformationstag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (134, '2014-11-19', 'Buß- und Bettag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (135, '2014-12-25', '1. Weihnachtstag', 'SACHSEN'); insert into holiday (id, day, name, federalstate) values (136, '2014-12-26', '2. Weihnachtstag', 'SACHSEN'); -- 2014 - Sachsen-Anhalt insert into holiday (id, day, name, federalstate) values (137, '2014-01-01', 'Neujahr', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (138, '2014-01-06', 'Helige Drei Könige', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (139, '2014-04-18', 'Karfreitag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (140, '2014-04-21', 'Ostermontag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (141, '2014-05-01', 'Tag der Arbeit', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (142, '2014-05-29', 'Christi Himmelfahrt', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (143, '2014-06-09', 'Pfingstmontag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (144, '2014-10-03', 'Tag der Deutschen Einheit', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (145, '2014-10-31', 'Reformationstag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (146, '2014-12-25', '1. Weihnachtstag', 'SACHSEN_ANHALT'); insert into holiday (id, day, name, federalstate) values (147, '2014-12-26', '2. Weihnachtstag', 'SACHSEN_ANHALT'); -- 2014 - Schleswig-Holstein insert into holiday (id, day, name, federalstate) values (148, '2014-01-01', 'Neujahr', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (149, '2014-04-18', 'Karfreitag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (150, '2014-04-21', 'Ostermontag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (151, '2014-05-01', 'Tag der Arbeit', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (152, '2014-05-29', 'Christi Himmelfahrt', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (153, '2014-06-09', 'Pfingstmontag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (154, '2014-10-03', 'Tag der Deutschen Einheit', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (155, '2014-12-25', '1. Weihnachtstag', 'SCHLESWIG_HOLSTEIN'); insert into holiday (id, day, name, federalstate) values (156, '2014-12-26', '2. Weihnachtstag', 'SCHLESWIG_HOLSTEIN'); -- 2014 - Thüringen insert into holiday (id, day, name, federalstate) values (157, '2014-01-01', 'Neujahr', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (158, '2014-04-18', 'Karfreitag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (159, '2014-04-21', 'Ostermontag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (160, '2014-05-01', 'Tag der Arbeit', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (161, '2014-05-29', 'Christi Himmelfahrt', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (162, '2014-06-09', 'Pfingstmontag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (163, '2014-10-03', 'Tag der Deutschen Einheit', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (164, '2014-10-31', 'Tag der Deutschen Einheit', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (165, '2014-12-25', 'Reformationstag', 'THUERINGEN'); insert into holiday (id, day, name, federalstate) values (166, '2014-12-26', '2. Weihnachtstag', 'THUERINGEN'); ================================================ FILE: src/main/resources/db/migration/V4__add_invoices.sql ================================================ create table Invoice ( id int8 not null, creationDate date, dueDate date, identifier varchar(255), invoiceState varchar(255), invoiceTotal numeric(19, 2), version int4, debitor int8, primary key (id), unique (identifier) ); alter table Invoice add constraint FKD80EDB0D1D75383C foreign key (debitor) references Company; ================================================ FILE: src/main/resources/db/migration/V5__modify_vacationrequests.sql ================================================ alter table VacationRequest alter column approvalDate type timestamp; ================================================ FILE: src/main/resources/db/migration/V6__modify_travel_expense_reports_and_contact_persons.sql ================================================ alter table TravelExpenseReport add column submissionDate timestamp; alter table ContactPerson add column roles varchar(255); ================================================ FILE: src/main/resources/db/migration/V7__update_travel_expense_report_submission_date_from_travel_expenses.sql ================================================ UPDATE TravelExpenseReport ter SET submissionDate = ( SELECT MAX(te.submissionDate) FROM TravelExpense te WHERE te.report_id = ter.id ) WHERE ter.submissionDate IS NULL; ================================================ FILE: src/main/resources/db/migration/V8__add_sick_days.sql ================================================ create table SickDays ( id int8 not null, endDate date, startDate date, version int4, employee_id int8, primary key (id) ); alter table SickDays add constraint FKF63D6D555198E9B foreign key (employee_id) references Employee; ================================================ FILE: src/main/resources/db/migration/V9__modify_travel_expense_report.sql ================================================ alter table TravelExpenseReport add column approvalDate timestamp; alter table TravelExpenseReport add column approver_id int8; alter table TravelExpenseReport add constraint FK854DBA9248918324 foreign key (approver_id) references Employee; ================================================ FILE: src/main/resources/i18n/trackr-de.json ================================================ { "AUTHORITY": { "ROLE_ADMIN": "Administrator", "ROLE_SUPERVISOR": "Supervisor", "ROLE_EMPLOYEE": "Angestellter", "ROLES": "Rollen" }, "COMPANY": { "CREATE_NEW": "Neue Firma anlegen", "COMPANIES": "Firmen", "COMPANY_ID": "Eindeutiger Bezeichner", "NAME": "Name", "COMPANY_ID_CONFLICT": "Bezeichner bereits vergeben.", "TIME_FOR_PAYMENT": "Zahlungsziel" }, "ADDRESS": { "ADDRESS": "Adresse", "STREET": "Straße", "HOUSE_NUMBER": "Hausnummer", "CITY": "Stadt", "ZIPCODE": "PLZ", "COUNTRY": " Land" }, "CONTACTPERSON": { "CONTACTPERSON": "Kontaktperson", "CONTACTPERSONS": "Kontaktpersonen", "CREATE_NEW": "Neue Kontaktperson", "SALUTATION": " Anrede", "FIRST_NAME": "Vorname", "LAST_NAME": "Nachname", "EMAIL": "Email", "PHONE": "Telefon", "ROLES": "Rollen" }, "EMPLOYEE": { "CREATE_NEW": "Neuen Angestellten anlegen", "EMPLOYEES": "Angestellte", "EMPLOYEE": "Angestellte/r", "FIRST_NAME": "Vorname", "LAST_NAME": "Nachname", "TITLE": "Titel", "SALARY": "Gehalt", "PHONE_NUMBER": "Telefon", "HOURLY_COST_RATE": "Stundensatz", "ADMINISTRATE_ROLES": "Alle Rollen", "JOIN_DATE": "Eintrittsdatum", "LEAVE_DATE": "Austrittsdatum", "FEDERAL_STATE": "Bundesland", "VACATION_ENTITLEMENT": "Urlaubsanspruch", "DELETED": "gelöscht" }, "CREDENTIAL": { "EMAIL": "E-Mail", "ENABLED": "Aktiv", "EMAIL_CONFLICT": "Email bereits in Benutzung." }, "PROJECT": { "CREATE_NEW": "Neues Projekt anlegen", "PROJECTS": "Projekte", "PROJECT": "Projekt", "IDENTIFIER": "Eindeutiger Bezeichner", "NAME": "Name", "VOLUME": "Volumen", "HOURLY_RATE": "Stundensatz", "DAILY_RATE": "Tagessatz", "FIXED_PRICE": "Festpreis", "COMPANY": "Firma", "DEBITOR": "Debitor", "IDENTIFIER_CONFLICT": "Bezeichner bereits in Verwendung" }, "WORKTIME": { "DATE": "Datum", "PROJECT": "Projekt", "START": "Startzeit", "END": "Endzeit", "COMMENT": "Kommentar", "TOTAL_HOURS": "Stunden gesamt" }, "BILLABLE_TIME": { "DATE": "Datum", "BILLABLE_HOURS": "Rechnungsstunden" }, "VACATION_REQUEST": { "START_DATE": "Start", "END_DATE": "Ende", "NUMBER_OF_DAYS": "Tage", "STATUS": "Status", "APPROVER": "Genehmigt von", "APPROVAL_DATE": "Genehmigt am", "PENDING": "Wartet", "APPROVED": "Genehmigt", "REJECTED": "Abgelehnt", "INTERVAL": "Zeitraum", "EMPLOYEE": "Angestellter", "SUBMITTED": "Eingereicht am", "APPROVER_SYSTEM": "automatisch" }, "TRAVEL_EXPENSE": { "TYPE": "Typ", "TYPE_VALUES": { "HOTEL": "Hotel", "TAXI": "Taxi", "FLIGHT": "Flug", "TRAIN": "Zugfahrt", "PARKING": "Parken", "OEPNV": "ÖPNV", "MILEAGE_ALLOWANCE": "Kilometerpauschale", "OTHER": "Andere" }, "FROM_DATE": "Von", "TO_DATE": "Bis", "VAT": "Mehrwertsteuer", "COST": "Gesamt", "COMMENT": "Kommentar", "SUBMITTED": "Eingereicht am", "PAID": "Bezahlt" }, "TRAVEL_EXPENSE_REPORT": { "TRAVEL_EXPENSE_REPORT": "Reisekostenbericht", "TRAVEL_EXPENSE_REPORTS": "Reisekostenberichte", "TOTAL": "Summe", "PENDING": "Wartend", "APPROVED": "Akzeptiert", "REJECTED": "Abgelehnt", "SUBMITTED": "Abgeschickt", "ID": "Nummber", "EMPLOYEE": "Angesteller", "APPROVER": "Genehmiger", "SUBMISSION_DATE": "Eingereicht am", "APPROVAL_DATE": "Genehmigt am", "COMMENTS": "Kommentare", "DEBITOR": "Debitor", "PROJECT": "Projekt" }, "INVOICE": { "INVOICE": "Rechnung", "INVOICES": "Rechnungen", "CREATE_NEW": "Neue Rechnung", "IDENTIFIER": "Eindeutiger Bezeichner", "INVOICE_TOTAL": "Gesamtbetrag", "DUE_DATE": "Fälligkeitsdatum", "STATE": "Status", "DEBITOR": "Debitor", "CREATION_DATE": "Ausstellungsdatum", "OUTSTANDING": "Ausstehend", "PAID": "Bezahlt", "OVERDUE": "Überfällig", "IDENTIFIER_CONFLICT": "Bezeichner schon in Nutzung.", "INVOICE_CREATED": "Rechnung angelegt." }, "SICK_DAYS": { "START_DATE": "Startdatum", "END_DATE": "Enddate", "TOTAL_DAYS": "Anzahl Tage" }, "DIRECTIVES": { "COMMENT_SECTION": { "COMMENTS": "Kommentare", "TEXT": "Text" } }, "PAGES": { "HOME": { "WELCOME_TEXT": "Willkommen bei trackr", "BREADCRUMB": "Home" }, "ADMINISTRATION": { "TITLE": "Verwaltung", "TEXT_COMPANIES": "Legen Sie neue Firmen an und editieren Sie bestehende.", "TEXT_EMPLOYEES": "Verwalten Sie Angestellte in trackr.", "TEXT_PROJECTS": "Verwalten Sie Projekte" }, "EMPLOYEE": { "TITLE": "Self Services", "TEXT_EDIT_PROFILE": "Editieren Sie ihr Profil", "TEXT_EDIT_TIMESHEET": "Erfassen Sie Arbeitszeiten", "TEXT_VACATION": "Urlaubsanträge", "EXPENSES": { "TITLE": "Reisekosten", "DELETE_FORBIDDEN": "Dieser Report darf nicht gelöscht werden.", "REIMBURSEMENT": "Rückerstattung" }, "TEXT_TIMESHEET_OVERVIEW": "Arbeitszeiten Übersicht", "TIMESHEET_OVERVIEW": { "GROUPED_BY_PROJECT": "Nach Projekt gruppiert", "HOURS_PER_DAY": "Stunden pro Tag", "TOTAL": "Gesamt" }, "ADDRESS_BOOK": { "TITLE": "Adressbuch" }, "SICK_DAYS": { "TITLE": "Krankheitstage" } }, "SUPERVISOR": { "TITLE": "Supervisor", "TEXT_BILL": "Rechnungszeiten erfassen", "TEXT_BILL_CREATE": "Rechnung erstellen", "BILL": { "TITLE": "Rechnungszeiten erfassen", "HOURS": "Stunden", "SET_ALL": "Alle setzen", "SUM": "Summe", "TRANSFER_HOURS": "Aus Stunden übernehmen" }, "VACATION": { "TITLE": "Urlaubsanträge", "REALLY_DELETE": "Wollen Sie diesen Urlaubsantrag wirklich entfernen?" }, "BILL_CREATE": { "TITLE": "Rechnung erstellen", "HOURS": "Stunden", "MAN_DAYS": "Manntage", "NET_PRICE": "Netto", "GROSS_PRICE": "Brutto" }, "TRAVEL_EXPENSE_REPORTS": { "TITLE": "Reisekosten", "EMPTY_TEXT": "Keine Reisekostenberichte", "JUMP_TO_REPORT": "zu Report wechseln", "APPROVED_BY": "Genehmigt von" }, "TRAVEL_EXPENSE_REPORT": { "NOT_FOUND": "Reisekostenbericht #{{id}} nicht gefunden.", "BACK_TO_LIST": "Zurück zur Liste." } }, "REPORTR": { "REVENUE": { "TITLE": "Einnahmen", "TURNOVER": "Umsatz", "TOTAL": "Gesamt" }, "VACATION": { "TITLE": "Urlaub", "DAYS": "Tage" }, "PROJECT_HOURS": { "TITLE": "Projektstunden", "TRACKED_HOURS": "Erfasste Stunden", "BILLED_HOURS": "Berechnete Stunden" }, "EMPLOYEE_HOURS": { "TITLE": "Arbeitsstunden", "WORKED_HOURS": "Gearbeitete Stunden" }, "TRAVEL_EXPENSE": { "TITLE": "Reisekosten", "EXPENSES": "Ausgaben" }, "SICK_DAYS": { "TITLE": "Krankheitstage" }, "EXPENSES_DEBITOR": { "TITLE": "Reisekosten nach Debitor" } } }, "ACTIONS": { "OK": "Ok", "CONFIRM": "Bestätigen", "GO": "Los", "ACTIONS": "Aktionen", "SAVE": "Speichern", "NEW": "Neu", "CANCEL": "Abbrechen", "EDIT": "Editieren", "CLOSE": "Schließen", "CLEAR": "Löschen", "RESET": "Zurücksetzen", "APPROVE": "Genehmigen", "REJECT": "Ablehnen", "SUBMIT": "Abschicken", "REMOVE": "Löschen", "ADD": "Hinzufügen", "BACK": "Zurück", "SEARCH": "Suchen", "DELETE": "Löschen", "REALLY_DELETE": "Wirklich löschen?", "DOWNLOAD": "Herunterladen" }, "FIELDS": { "OPTIONAL": "optional" }, "DATE": { "TODAY": "Heute", "WEEKS": "Wochen" }, "LANGUAGE": "Sprache", "ERRORS": { "FAILED_REQUEST": "Aufruf fehlgeschlagen." } } ================================================ FILE: src/main/resources/i18n/trackr-en.json ================================================ { "AUTHORITY": { "ROLE_ADMIN": "Administrator", "ROLE_SUPERVISOR": "Supervisor", "ROLE_EMPLOYEE": "Employee", "ROLES": "Roles" }, "COMPANY": { "CREATE_NEW": "Create new company", "COMPANIES": "Companies", "COMPANY_ID": "Unique identifier", "NAME": "Name", "COMPANY_ID_CONFLICT": "Identifier already in use.", "TIME_FOR_PAYMENT": "Time for payment" }, "ADDRESS": { "ADDRESS": "Address", "STREET": "Street", "HOUSE_NUMBER": "House number", "CITY": "City", "ZIPCODE": "Zipcode", "COUNTRY": "Country" }, "CONTACTPERSON": { "CONTACTPERSON": "Contact person", "CONTACTPERSONS": "Contact persons", "CREATE_NEW": "New contact person", "SALUTATION": " Salutation", "FIRST_NAME": "First name", "LAST_NAME": "Last name", "EMAIL": "Email", "PHONE": "Phone", "ROLES": "Roles" }, "EMPLOYEE": { "CREATE_NEW": "Create new employee", "EMPLOYEES": "Employees", "EMPLOYEE": "Employee", "FIRST_NAME": "First name", "LAST_NAME": "Last name", "TITLE": "Title", "SALARY": "Salary", "PHONE_NUMBER": "Phone number", "HOURLY_COST_RATE": "Hourly cost rate", "ADMINISTRATE_ROLES": "All roles", "JOIN_DATE": "Join date", "LEAVE_DATE": "Leave date", "FEDERAL_STATE": "Federal state", "VACATION_ENTITLEMENT": "Vacation entitlement", "DELETED": "deleted" }, "CREDENTIAL": { "EMAIL": "E-Mail", "ENABLED": "Enabled", "EMAIL_CONFLICT": "Email already in use." }, "PROJECT": { "CREATE_NEW": "Create new project", "PROJECTS": "Projects", "PROJECT": "Project", "IDENTIFIER": "Identifier", "NAME": "Name", "VOLUME": "Volume", "HOURLY_RATE": "Hourly rate", "DAILY_RATE": "Daily rate", "FIXED_PRICE": "Fixed price", "COMPANY": "Company", "DEBITOR": "Debitor", "IDENTIFIER_CONFLICT": "Identifier in use." }, "BILLABLE_TIME": { "DATE": "Date", "BILLABLE_HOURS": "Billable hours" }, "VACATION_REQUEST": { "START_DATE": "Start date", "END_DATE": "End date", "NUMBER_OF_DAYS": "Number of days", "STATUS": "Status", "APPROVER": "Approved by", "APPROVAL_DATE": "Approval date", "PENDING": "Pending", "APPROVED": "Approved", "REJECTED": "Rejected", "INTERVAL": "Interval", "EMPLOYEE": "Employee", "SUBMITTED": "Submitted", "APPROVER_SYSTEM": "system" }, "TRAVEL_EXPENSE": { "TYPE": "Type", "TYPE_VALUES": { "HOTEL": "Hotel", "TAXI": "Taxi", "FLIGHT": "Flight", "TRAIN": "Train", "PARKING": "Parking", "OEPNV": "ÖPNV", "MILEAGE_ALLOWANCE": "Mileage allowance", "OTHER": "Other" }, "FROM_DATE": "From", "TO_DATE": "To", "VAT": "VAT", "COST": "Total", "COMMENT": "Comment", "SUBMITTED": "Submitted", "PAID": "Paid" }, "TRAVEL_EXPENSE_REPORT": { "TRAVEL_EXPENSE_REPORT": "Travel expense report", "TRAVEL_EXPENSE_REPORTS": "Travel expense reports", "TOTAL": "Total", "PENDING": "Pending", "APPROVED": "Approved", "REJECTED": "Rejected", "SUBMITTED": "Submitted", "ID": "Id", "EMPLOYEE": "Employee", "APPROVER": "Approver", "SUBMISSION_DATE": "Submission date", "APPROVAL_DATE": "Approval date", "COMMENTS": "Comments", "DEBITOR": "Debitor", "PROJECT": "Project" }, "INVOICE": { "INVOICE": "Invoice", "INVOICES": "Invoices", "CREATE_NEW": "New invoice", "IDENTIFIER": "Identifier", "INVOICE_TOTAL": "Total", "DUE_DATE": "Due date", "STATE": "State", "DEBITOR": "Debitor", "CREATION_DATE": "Creation date", "OUTSTANDING": "Outstanding", "PAID": "Paid", "OVERDUE": "Overdue", "IDENTIFIER_CONFLICT": "Identifier already in use.", "INVOICE_CREATED": "Invoice created." }, "WORKTIME": { "DATE": "Date", "PROJECT": "Project", "START": "Start time", "END": "End time", "COMMENT": "Comment", "TOTAL_HOURS": "Total hours" }, "SICK_DAYS": { "START_DATE": "Start date", "END_DATE": "End date", "TOTAL_DAYS": "Total days" }, "DIRECTIVES": { "COMMENT_SECTION": { "COMMENTS": "Comments", "TEXT": "Text" } }, "PAGES": { "HOME": { "WELCOME_TEXT": "Welcome to trackr", "BREADCRUMB": "Home" }, "ADMINISTRATION": { "TITLE": "Administration", "TEXT_COMPANIES": "Edit and create companies.", "TEXT_EMPLOYEES": "Administrate employees in trackr.", "TEXT_PROJECTS": "Administrate projects" }, "EMPLOYEE": { "TITLE": "Self Services", "TEXT_EDIT_PROFILE": "Edit your profile", "TEXT_EDIT_TIMESHEET": "Track times", "TEXT_VACATION": "Vacation request", "EXPENSES": { "TITLE": "Travel expenses", "DELETE_FORBIDDEN": "You cannot delete this report.", "REIMBURSEMENT": "Reimbursement" }, "TEXT_TIMESHEET_OVERVIEW": "Working times overview", "TIMESHEET_OVERVIEW": { "GROUPED_BY_PROJECT": "Grouped by project", "HOURS_PER_DAY": "Hours per day", "TOTAL": "Total" }, "ADDRESS_BOOK": { "TITLE": "Address book" }, "SICK_DAYS": { "TITLE": "Sick days" } }, "SUPERVISOR": { "TITLE": "Supervisor", "TEXT_BILL": "Enter billable hours", "TEXT_BILL_CREATE": "Create bill", "BILL": { "TITLE": "Track billable hours", "HOURS": "Hours", "SET_ALL": "Set all", "SUM": "Sum", "TRANSFER_HOURS": "Transfer from hours" }, "BILL_CREATE": { "TITLE": "Create bill", "HOURS": "Hours", "MAN_DAYS": "Man days", "NET_PRICE": "Net price", "GROSS_PRICE": "Gross price" }, "VACATION": { "TITLE": "Vacation requests", "REALLY_DELETE": "Do you really want to remove the vacation request?" }, "TRAVEL_EXPENSE_REPORTS": { "TITLE": "Travel expenses", "EMPTY_TEXT": "No submitted travel expense reports.", "JUMP_TO_REPORT": "jump to report", "APPROVED_BY": "Approved by" }, "TRAVEL_EXPENSE_REPORT": { "NOT_FOUND": "Travel expense report #{{id}} not found.", "BACK_TO_LIST": "Back to list." } }, "REPORTR": { "REVENUE": { "TITLE": "Revenues", "TURNOVER": "Turnover", "TOTAL": "Total" }, "VACATION": { "TITLE": "Vacation", "DAYS": "Days" }, "PROJECT_HOURS": { "TITLE": "Project hours", "TRACKED_HOURS": "Tracked hours", "BILLED_HOURS": "Billed hours" }, "EMPLOYEE_HOURS": { "TITLE": "Employee hours", "WORKED_HOURS": "Worked hours" }, "TRAVEL_EXPENSE": { "TITLE": "Travel expenses", "EXPENSES": "Expenses" }, "SICK_DAYS": { "TITLE": "Sick days" }, "EXPENSES_DEBITOR": { "TITLE": "Expenses by debitor" } }, "BREADCRUMB_UNKNOWN": "unknown" }, "ACTIONS": { "OK": "Ok", "CONFIRM": "Confirm", "GO": "Go", "ACTIONS": "Actions", "SAVE": "Save", "NEW": "New", "CANCEL": "Cancel", "EDIT": "Edit", "CLOSE": "Close", "CLEAR": "Clear", "RESET": "Reset", "APPROVE": "Approve", "REJECT": "Reject", "SUBMIT": "Submit", "REMOVE": "Remove", "ADD": "Add", "BACK": "Back", "SEARCH": "Search", "DELETE": "Delete", "REALLY_DELETE": "Really delete?", "DOWNLOAD": "Download" }, "FIELDS": { "OPTIONAL": "optional" }, "DATE": { "TODAY": "Today", "WEEKS": "Weeks" }, "LANGUAGE": "Language", "ERRORS": { "FAILED_REQUEST": "Request failed." } } ================================================ FILE: src/main/resources/i18n/validation/messages_de.properties ================================================ validation.date.endAfterBegin=Start darf nicht nach Ende sein validation.company.projectBelongsTo=Das Projekt muss zu der Firma gehören. ================================================ FILE: src/main/resources/i18n/validation/messages_en.properties ================================================ validation.date.endAfterBegin=Start must not be after end. validation.company.projectBelongsTo=The project must belong to the company. ================================================ FILE: src/main/resources/logback-console.xml ================================================ ${charset} ${pattern} ================================================ FILE: src/main/resources/logback-file.xml ================================================ /opt/techdev/trackr-backend/logs/trackr-backend.log /opt/techdev/trackr-backend/logs/trackr-backend_%d{yyyy-MM-dd}.%i.log 5MB 30 ${charset} ${pattern} ================================================ FILE: src/main/resources/pdfTemplates/travel-expenses/report.html ================================================

Travel Expense Report #

Submitted by: Mister Techdev
Debitor: Techdev
Project: Techdev
Period: 10.10.2013 - 13.10.2013
Total: 500 €
Reimbursement: 500 €
Type From To VAT Total Paid Comment
TAXI 10.10.2013 13.10.2013 12% 34 € x Comment
Date: 24.07.1983





Signature
================================================ FILE: src/test/java/de/techdev/test/FlywayTest.java ================================================ package de.techdev.test; import org.flywaydb.core.Flyway; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; @RunWith(SpringRunner.class) @SpringBootTest(classes = {FlywayTest.FlywayTestConfig.class, FlywayAutoConfiguration.FlywayConfiguration.class}) public class FlywayTest { public static class FlywayTestConfig { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build(); } } @Autowired private Flyway flyway; @Test public void migrationSucceeds() { flyway.migrate(); } } ================================================ FILE: src/test/java/de/techdev/test/InMemoryOAuth2ResourceServerConfiguration.java ================================================ package de.techdev.test; import de.techdev.trackr.core.security.OAuth2ResourceServerConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; import javax.sql.DataSource; /** * Uses the real OAuth2 resource server configuration but with an in memory token store so we can easily modifiy it in tests. */ @Configuration @Profile("test-oauth") public class InMemoryOAuth2ResourceServerConfiguration extends OAuth2ResourceServerConfiguration { @Override public DataSource oauthDataSource() { return null; } @Override public TokenStore tokenStore() { return new InMemoryTokenStore(); } } ================================================ FILE: src/test/java/de/techdev/test/TestConstants.java ================================================ package de.techdev.test; public class TestConstants { /** * Path to the SQL file that creates the uuid mapping table for {@link @org.springframework.test.context.jdbc.Sql} in tests. */ public static final String CREATE_UUID_MAPPING_TABLE_SQL_FILE = "/de/techdev/trackr/domain/tableUuidMapping.sql"; } ================================================ FILE: src/test/java/de/techdev/test/TransactionalIntegrationTest.java ================================================ package de.techdev.test; import de.techdev.trackr.Trackr; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; /** * A test that should rollback all transactions after finishing. */ @SpringBootTest(classes = { Trackr.class }) @RunWith(SpringRunner.class) @ActiveProfiles(value = {"in-memory-database"}) @Transactional @TransactionConfiguration public abstract class TransactionalIntegrationTest { } ================================================ FILE: src/test/java/de/techdev/test/oauth/OAuthRequest.java ================================================ package de.techdev.test.oauth; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Retention(RUNTIME) @Target({ TYPE, METHOD }) public @interface OAuthRequest { /** * The roles for the OAuth access token */ String[] value() default {"ROLE_EMPLOYEE"}; /** * The username for the OAuth access token */ String username() default "employee@techdev.de"; } ================================================ FILE: src/test/java/de/techdev/test/oauth/OAuthTestExecutionListener.java ================================================ package de.techdev.test.oauth; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.test.context.TestContext; import org.springframework.test.context.support.AbstractTestExecutionListener; import javax.annotation.Nonnull; import java.util.Collection; import java.util.HashMap; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static org.echocat.jomon.runtime.CollectionUtils.asSet; /** * Test execution listener that adds an OAuth token into the store before each method. The value of the token can be set with the * {@link OAuthRequest} annotation on each test method. */ @Slf4j public class OAuthTestExecutionListener extends AbstractTestExecutionListener { public static final String OAUTH_TOKEN_VALUE = "token"; private final OAuth2AccessToken accessToken; private final OAuth2Request clientAuthentication; public OAuthTestExecutionListener() { accessToken = new DefaultOAuth2AccessToken(OAUTH_TOKEN_VALUE); // TODO no magic constants! These should probably come from the OAuthResourceServer Configuration! clientAuthentication = new OAuth2Request(new HashMap<>(), "trackr-page", null, true, asSet("read", "write"), asSet("techdev-services"), null, null, null); } @Override public void beforeTestMethod(TestContext testContext) throws Exception { OAuthRequest annotation = AnnotationUtils.getAnnotation(testContext.getTestMethod(), OAuthRequest.class); if (annotation == null) { annotation = AnnotationUtils.getAnnotation(testContext.getTestClass(), OAuthRequest.class); } if(annotation == null) { log.warn("No OAuthToken annotation for {} in class {}, did you forget it?", testContext.getTestMethod().getName(), testContext.getTestClass().getName()); } else { insertOauthTokenIntoStore(testContext, annotation); } } private void insertOauthTokenIntoStore(TestContext testContext, @Nonnull OAuthRequest token) { TokenStore tokenStore = testContext.getApplicationContext().getBean(TokenStore.class); tokenStore.storeAccessToken(accessToken, new OAuth2Authentication(clientAuthentication, getAuthenticationFromAnnotation(token))); } private Authentication getAuthenticationFromAnnotation(OAuthRequest token) { return new Authentication() { @Override public Collection getAuthorities() { return asList(token.value()) .stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toSet()); } @Override public Object getCredentials() { return null; } @Override public Object getDetails() { return null; } @Override public Object getPrincipal() { return new User(getName(), "", getAuthorities()); } @Override public boolean isAuthenticated() { return true; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { } @Override public String getName() { return token.username(); } }; } @Override public void afterTestMethod(TestContext testContext) throws Exception { TokenStore tokenStore = testContext.getApplicationContext().getBean(TokenStore.class); tokenStore.removeAccessToken(accessToken); } } ================================================ FILE: src/test/java/de/techdev/test/rest/AbstractDomainResourceSecurityTest.java ================================================ package de.techdev.test.rest; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import static java.util.Arrays.asList; public abstract class AbstractDomainResourceSecurityTest extends AbstractRestIntegrationTest { /** * An SQL file that empties all tables in the trackr database. */ public static final String EMPTY_DATABASE_FILE = "/de/techdev/trackr/domain/emptyDatabase.sql"; protected abstract String getResourceName(); private HttpEntity getJsonEntity(String content) { MultiValueMap headers = new LinkedMultiValueMap() {{ put("Content-Type", asList("application/json; charset=utf-8")); }}; return new HttpEntity<>(content, headers); } protected ResponseEntity root() throws Exception { return restTemplate.getForEntity(host + "/" + getResourceName(), String.class); } protected ResponseEntity one(Long id) throws Exception { return oneUrl("/" + getResourceName() + "/" + id); } protected ResponseEntity oneUrl(String url) throws Exception { return restTemplate.getForEntity(host + url, String.class); } protected ResponseEntity create(String payload) throws Exception { return restTemplate.exchange(host + "/" + getResourceName() + "/", HttpMethod.POST, getJsonEntity(payload), String.class); } protected ResponseEntity update(Long id, String payload) throws Exception { return restTemplate.exchange(host + "/" + getResourceName() + "/" + id, HttpMethod.PUT, getJsonEntity(payload), String.class); } /** * Perform a PUT on a link of a random resource (with header Content-Type: text/uri-list) * * @param linkName The name of the link, will be appended to the URI of the resource (e.g. /company/0/contactPersons -> linkName = contactPersons). * @param linkContent The content to PUT, e.g. /contactPersons/0 */ protected ResponseEntity updateLink(Long id, String linkName, String linkContent) throws Exception { LinkedMultiValueMap headers = new LinkedMultiValueMap<>(); headers.put("Content-Type", asList("text/uri-list")); HttpEntity request = new HttpEntity<>(linkContent, headers); return restTemplate.exchange(host + "/" + getResourceName() + "/" + id + "/" + linkName, HttpMethod.PUT, request, String.class); } protected ResponseEntity updateViaPatch(Long id, String patch) throws Exception { return restTemplate.exchange(host + "/" + getResourceName() + "/" + id, HttpMethod.PATCH, getJsonEntity(patch), String.class); } protected ResponseEntity remove(Long id) throws Exception { return removeUrl("/" + getResourceName() + "/" + id); } protected ResponseEntity removeUrl(String url) throws Exception { return restTemplate.exchange(host + url, HttpMethod.DELETE, HttpEntity.EMPTY, Void.class); } } ================================================ FILE: src/test/java/de/techdev/test/rest/AbstractJsonGenerator.java ================================================ package de.techdev.test.rest; import javax.json.Json; import javax.json.stream.JsonGeneratorFactory; import java.util.function.Consumer; /** * Generate JSON from one of our entities. Just common operations. * @param The entity class like Employee or Project. * @param The type of the builder so we can return it in the base build methods with the correct type. */ public abstract class AbstractJsonGenerator { private T object; protected final JsonGeneratorFactory jsonGeneratorFactory; public AbstractJsonGenerator() { jsonGeneratorFactory = Json.createGeneratorFactory(null); } public B start() { reset(); object = getNewTransientObject(500); return getSelf(); } /** * With this function you can change the created object in any way you like with a lambda. * @param function Function to change the state of the object being generated. */ public final B apply(Consumer function) { function.accept(object); return getSelf(); } /** * @return A String containing the JSON representation of the object. */ public String build() { return getJsonRepresentation(object); } /** * The actual transformation to JSON. */ protected abstract String getJsonRepresentation(T object); /** * Generate a new object with prefilled fields. * @param i Is used to add a number to fields that maybe must be unique. */ protected abstract T getNewTransientObject(int i); /** * Just a technical method to get the correct type of the builder in the base builder methods.. */ protected abstract B getSelf(); /** * If a new build starts and this builder needs a reset you can do it here. */ protected void reset() { // default no-op } } ================================================ FILE: src/test/java/de/techdev/test/rest/AbstractRestIntegrationTest.java ================================================ package de.techdev.test.rest; import de.techdev.test.InMemoryOAuth2ResourceServerConfiguration; import de.techdev.test.oauth.OAuthTestExecutionListener; import de.techdev.trackr.Trackr; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestTemplate; import javax.json.Json; import javax.json.stream.JsonGeneratorFactory; /** * Abstract test class to run tests that access the running REST API. */ @RunWith(SpringRunner.class) @SpringBootTest(classes = {Trackr.class, InMemoryOAuth2ResourceServerConfiguration.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles({"in-memory-database", "test-oauth", "granular-security"}) @TestExecutionListeners(value = OAuthTestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public abstract class AbstractRestIntegrationTest { @Value("${local.server.port}") private Integer serverPort; protected JsonGeneratorFactory jsonGeneratorFactory; protected RestTemplate restTemplate; protected String host; @Before public void setUpMvcFields() throws Exception { jsonGeneratorFactory = Json.createGeneratorFactory(null); restTemplate = new TestRestTemplate(OAuthTestExecutionListener.OAUTH_TOKEN_VALUE); host = "http://localhost:" + serverPort; } } ================================================ FILE: src/test/java/de/techdev/test/rest/DomainResourceTestMatchers.java ================================================ package de.techdev.test.rest; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import static org.junit.Assert.assertEquals; public class DomainResourceTestMatchers { private DomainResourceTestMatchers() { } /** * Functional interface for a consumer that can throw an exception. * @param The type to consume. */ @FunctionalInterface private static interface ConsumerWithException { public void accept(T item) throws Exception; } private static class ResultActionsMatcher extends TypeSafeMatcher { private String description; private ConsumerWithException test; protected ResultActionsMatcher(String description, ConsumerWithException test) { this.description = description; this.test = test; } @Override protected boolean matchesSafely(ResponseEntity item) { try { test.accept(item); return true; } catch (Exception e) { return false; } } @Override public void describeTo(Description description) { description.appendText(this.description); } } /** * @return Matcher that checks if the HTTP status is 200. */ public static Matcher isAccessible() { return new ResultActionsMatcher("accessible", response -> assertEquals(HttpStatus.OK, response.getStatusCode())); } /** * @return Matcher that checks if the HTTP status is 201 and the "id" field in the returned JSON is not null. */ public static Matcher isCreated() { return new ResultActionsMatcher("created", response -> assertEquals(HttpStatus.CREATED, response.getStatusCode())); } /** * @return Matcher that checks if the HTTP status is 403. */ public static Matcher isForbidden() { return new ResultActionsMatcher("forbidden", response -> assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode())); } /** * @return Matcher that checks if the HTTP status is 200 and the "id" field in the returned JSON is not null. */ public static Matcher isUpdated() { return new ResultActionsMatcher("updated", response -> assertEquals(HttpStatus.OK, response.getStatusCode())); } /** * @return Matcher that checks if the HTTP status is 204. */ public static Matcher isNoContent() { return new ResultActionsMatcher("no content", request -> assertEquals(HttpStatus.NO_CONTENT, request.getStatusCode())); } /** * @return Matcher that checks if the HTTP status is 405. */ public static Matcher isMethodNotAllowed() { return new ResultActionsMatcher("method not allowed", response -> assertEquals(HttpStatus.METHOD_NOT_ALLOWED, response.getStatusCode())); } } ================================================ FILE: src/test/java/de/techdev/test/rest/TestRestTemplate.java ================================================ package de.techdev.test.rest; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.protocol.HttpContext; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.client.*; import org.springframework.util.ClassUtils; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.net.URI; import java.util.*; /** * Convenient subclass of {@link org.springframework.web.client.RestTemplate} that is suitable for integration tests. * They are fault tolerant, and optionally can carry OAuth2 authentication headers. If * Apache Http Client 4.3.2 or better is available (recommended) it will be used as the * client, and by default configured to ignore cookies and redirects. * * @author Moritz Schulze * @author Dave Syer * @author Phillip Webb */ public class TestRestTemplate extends RestTemplate { /** * Create a new {@link TestRestTemplate} instance with the specified credentials. * @param token the token to use (or {@code null}) * @param httpClientOptions client options to use if the Apache HTTP Client is used */ public TestRestTemplate(String token, HttpClientOption... httpClientOptions) { if (ClassUtils.isPresent("org.apache.http.client.config.RequestConfig", null)) { setRequestFactory(new CustomHttpComponentsClientHttpRequestFactory( httpClientOptions)); } addAuthentication(token); setErrorHandler(new DefaultResponseErrorHandler() { @Override public void handleError(ClientHttpResponse response) throws IOException { } }); } private void addAuthentication(String token) { if (token == null) { return; } List interceptors = Collections . singletonList(new BearerAuthorizationInterceptor( token)); setRequestFactory(new InterceptingClientHttpRequestFactory(getRequestFactory(), interceptors)); } /** * Options used to customize the Apache Http Client if it is used. */ public static enum HttpClientOption { /** * Enable cookies. */ ENABLE_COOKIES, /** * Enable redirects. */ ENABLE_REDIRECTS } private static class BearerAuthorizationInterceptor implements ClientHttpRequestInterceptor { private final String token; public BearerAuthorizationInterceptor(String token) { this.token = token; } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { request.getHeaders().add("Authorization", "Bearer " + this.token); return execution.execute(request, body); } } protected static class CustomHttpComponentsClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory { private final String cookieSpec; private final boolean enableRedirects; public CustomHttpComponentsClientHttpRequestFactory( HttpClientOption[] httpClientOptions) { Set options = new HashSet<>( Arrays.asList(httpClientOptions)); this.cookieSpec = (options.contains(HttpClientOption.ENABLE_COOKIES) ? CookieSpecs.STANDARD : CookieSpecs.IGNORE_COOKIES); this.enableRedirects = options.contains(HttpClientOption.ENABLE_REDIRECTS); } @Override protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) { HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(getRequestConfig()); return context; } protected RequestConfig getRequestConfig() { Builder builder = RequestConfig.custom().setCookieSpec(this.cookieSpec) .setAuthenticationEnabled(false) .setRedirectsEnabled(this.enableRedirects); return builder.build(); } } } ================================================ FILE: src/test/java/de/techdev/trackr/core/web/converters/DateConverterTest.java ================================================ package de.techdev.trackr.core.web.converters; import org.junit.Before; import org.junit.Test; import java.text.SimpleDateFormat; import java.util.Date; import static org.echocat.jomon.testing.BaseMatchers.isNull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; /** * @author Moritz Schulze */ public class DateConverterTest { private DateConverter dateConverter; @Before public void setUp() throws Exception { dateConverter = new DateConverter(); } @Test public void convert10() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date expected = sdf.parse("2014-01-01"); Date date = dateConverter.convert("2014-01-01"); assertThat(date, is(expected)); } @Test public void convert19() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date expected = sdf.parse("2014-01-01 13:00:00"); Date date = dateConverter.convert("2014-01-01 13:00:00"); assertThat(date, is(expected)); } @Test public void convertNull() throws Exception { Date date = dateConverter.convert(null); assertThat(date, isNull()); } @Test(expected = IllegalArgumentException.class) public void convertWrongLength() throws Exception { dateConverter.convert("2014-01"); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/common/FederalStateControllerIntegrationTest.java ================================================ package de.techdev.trackr.domain.common; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static org.junit.Assert.assertThat; @OAuthRequest public class FederalStateControllerIntegrationTest extends AbstractRestIntegrationTest { @Test public void getAllFederalStates() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/federalStates", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/common/UuidMapperIntegrationTest.java ================================================ package de.techdev.trackr.domain.common; import de.techdev.test.TestConstants; import de.techdev.test.TransactionalIntegrationTest; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; import java.util.UUID; import static org.echocat.jomon.testing.BaseMatchers.isNotNull; import static org.echocat.jomon.testing.BaseMatchers.isNull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @Sql(TestConstants.CREATE_UUID_MAPPING_TABLE_SQL_FILE) public class UuidMapperIntegrationTest extends TransactionalIntegrationTest { @Autowired private UuidMapper uuidMapper; @Test public void insertUuid() throws Exception { UUID uuid = uuidMapper.createUUID(1L); assertThat(uuid, isNotNull()); } @Test public void findUuid() throws Exception { UUID uuid = uuidMapper.createUUID(1L); Long id = uuidMapper.getIdFromUUID(uuid.toString()); assertThat(id, is(1L)); } @Test public void deleteUuidByUuid() throws Exception { UUID uuid = uuidMapper.createUUID(1L); uuidMapper.deleteUUID(uuid.toString()); Long id = uuidMapper.getIdFromUUID(uuid.toString()); assertThat(id, isNull()); } @Test public void deleteById() throws Exception { UUID uuid = uuidMapper.createUUID(1L); uuidMapper.deleteUUID(1L); Long id = uuidMapper.getIdFromUUID(uuid.toString()); assertThat(id, isNull()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/common/UuidMapperTest.java ================================================ package de.techdev.trackr.domain.common; import org.junit.Before; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; public class UuidMapperTest { private UuidMapper uuidMapper; @Before public void setUp() throws Exception { uuidMapper = new UuidMapper(); } @Test public void extractUuid() throws Exception { String uuid = uuidMapper.extractUUIDFromString("7f08028d-700e-47b4-bec0-b82192d0f1c0"); assertThat(uuid, is("7f08028d-700e-47b4-bec0-b82192d0f1c0")); } @Test public void extractUuidWithPrefix() throws Exception { String uuid = uuidMapper.extractUUIDFromString("TEST 7f08028d-700e-47b4-bec0-b82192d0f1c0"); assertThat(uuid, is("7f08028d-700e-47b4-bec0-b82192d0f1c0")); } @Test public void extractUuidWithPrefixAndSuffix() throws Exception { String uuid = uuidMapper.extractUUIDFromString("TEST 7f08028d-700e-47b4-bec0-b82192d0f1c0 TEST2"); assertThat(uuid, is("7f08028d-700e-47b4-bec0-b82192d0f1c0")); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/AddressJsonGenerator.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; public class AddressJsonGenerator extends AbstractJsonGenerator { @Override protected String getJsonRepresentation(Address address) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); jg.writeStartObject() .write("version", address.getVersion()) .write("street", address.getStreet()) .write("houseNumber", address.getHouseNumber()) .write("zipCode", address.getZipCode()) .write("city", address.getCity()) .write("country", address.getCountry()); if (address.getId() != null) { jg.write("id", address.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected Address getNewTransientObject(int i) { Address address = new Address(); address.setVersion(0); address.setCity("city_" + i); address.setCountry("country_" + i); address.setHouseNumber(Integer.toString(i)); address.setStreet("street_" + i); address.setZipCode("zip_" + i); return address; } @Override protected AddressJsonGenerator getSelf() { return this; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/AddressResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("address/resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_ADMIN") public class AddressResourceSecurityTest extends AbstractDomainResourceSecurityTest { private AddressJsonGenerator jsonGenerator = new AddressJsonGenerator(); @Override protected String getResourceName() { return "addresses"; } @Test @OAuthRequest public void findAllNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test @OAuthRequest public void one() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void createAllowedForAdmin() throws Exception { String json = jsonGenerator.start().build(); assertThat(create(json), isCreated()); } @Test public void putAllowedForAdmin() throws Exception { String json = jsonGenerator.start().apply(c -> c.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test public void patchAllowedForAdmin() throws Exception { assertThat(updateViaPatch(0L, "{\"street\": \"test\"}"), isUpdated()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void createNotAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().build(); assertThat(create(json), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void putForbiddenForSupervisor() throws Exception { String json = jsonGenerator.start().apply(c -> c.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void patchForbiddenForSupervisor() throws Exception { assertThat(updateViaPatch(0L, "{\"street\": \"test\"}"), isForbidden()); } @Test public void deleteNotExported() throws Exception { assertThat(remove(0L), isMethodNotAllowed()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/CompanyJsonGenerator.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; public class CompanyJsonGenerator extends AbstractJsonGenerator { private Long addressId; public CompanyJsonGenerator withAddressId(Long addressId) { this.addressId = addressId; return this; } @Override protected String getJsonRepresentation(Company object) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); jg.writeStartObject() .write("name", object.getName()) .write("version", object.getVersion()) .write("companyId", object.getCompanyId()); if (addressId != null) { jg.write("address", "/api/addresses/" + addressId); } if (object.getId() != null) { jg.write("id", object.getId()); } if (object.getTimeForPayment() != null) { jg.write("timeForPayment", object.getTimeForPayment()); } jg.writeEnd().close(); return writer.toString(); } @Override protected Company getNewTransientObject(int i) { Company company = new Company(); company.setVersion(0); company.setName("name_" + i); company.setCompanyId((long) i); company.setTimeForPayment(i); return company; } @Override protected CompanyJsonGenerator getSelf() { return this; } @Override protected void reset() { addressId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/CompanyRepositoryTest.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.TransactionalIntegrationTest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.jdbc.Sql; @Sql("repositoryTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public class CompanyRepositoryTest extends TransactionalIntegrationTest { @Autowired private CompanyRepository companyRepository; @Test public void deleteWithContactPersons() throws Exception { companyRepository.delete(0L); } @Test public void deleteWithProject() throws Exception { companyRepository.delete(1L); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/CompanyResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_ADMIN") public class CompanyResourceSecurityTest extends AbstractDomainResourceSecurityTest { private CompanyJsonGenerator jsonGenerator = new CompanyJsonGenerator(); @Override protected String getResourceName() { return "companies"; } @Test @OAuthRequest public void rootAccessible() throws Exception { assertThat(root(), isAccessible()); } @Test @OAuthRequest public void one() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest public void findByNameLikeOrderByNameAsc() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/companies/search/findByNameLikeIgnoreCaseOrderByNameAsc?name=%webshop%", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest public void findByCompanyId() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/companies/search/findByCompanyId?companyId=1000", String.class); assertThat(response, isAccessible()); } @Test public void postAllowedForAdmin() throws Exception { String json = jsonGenerator.start().withAddressId(0L).build(); assertThat(create(json), isCreated()); } @Test public void putAllowedForAdmin() throws Exception { String json = jsonGenerator.start().apply(c -> c.setId(0L)).withAddressId(0L).build(); assertThat(update(0L, json), isUpdated()); } @Test public void patchAllowedForAdmin() throws Exception { assertThat(updateViaPatch(0L, "{\"name\": \"test\"}"), isUpdated()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void postForbiddenForSupervisor() throws Exception { String json = jsonGenerator.start().withAddressId(0L).build(); assertThat(create(json), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void putForbiddenForSupervisor() throws Exception { String json = jsonGenerator.start().apply(c -> c.setId(0L)).withAddressId(0L).build(); assertThat(update(0L, json), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void patchForbiddenForSupervisor() throws Exception { assertThat(updateViaPatch(0L, "{\"name\": \"test\"}"), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void addContactPersonSupervisor() throws Exception { assertThat(updateLink(0L, "contactPersons", "/contactPersons/0"), isNoContent()); } @Test @OAuthRequest public void addContactForbiddenForEmployee() throws Exception { assertThat(updateLink(0L, "contactPersons", "/contactPersons/0"), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteContactAllowedForSupervisor() throws Exception { assertThat(removeUrl("/companies/0/contactPersons/0"), isNoContent()); } @Test @OAuthRequest public void deleteContactNotAllowedForEmployee() throws Exception { assertThat(removeUrl("/companies/0/contactPersons/0"), isForbidden()); } @Test public void deleteAllowedForAdmin() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteForbiddenForSupervisor() throws Exception { assertThat(remove(0L), isForbidden()); } @Test @OAuthRequest @Ignore("See DATAREST-476") public void getAddress() throws Exception { assertThat(oneUrl("/companies/0/address"), isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/ContactPersonJsonGenerator.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; public class ContactPersonJsonGenerator extends AbstractJsonGenerator { private Long companyId; public ContactPersonJsonGenerator withCompanyId(Long companyId) { this.companyId = companyId; return this; } @Override protected String getJsonRepresentation(ContactPerson person) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); jg.writeStartObject() .write("version", person.getVersion()) .write("email", person.getEmail()) .write("firstName", person.getFirstName()) .write("lastName", person.getLastName()) .write("phone", person.getPhone()) .write("salutation", person.getSalutation()) .write("roles", person.getRoles()); if (person.getId() != null) { jg.write("id", person.getId()); } if (companyId != null) { jg.write("company", "/api/companies/" + companyId); } jg.writeEnd().close(); return writer.toString(); } @Override protected ContactPerson getNewTransientObject(int i) { ContactPerson person = new ContactPerson(); person.setVersion(0); person.setEmail("person@techdev.de_" + i); person.setFirstName("first_name_" + i); person.setLastName("last_name_" + i); person.setPhone("phone_" + i); person.setSalutation("salutation_" + i); person.setRoles("roles_" + i); return person; } @Override protected ContactPersonJsonGenerator getSelf() { return this; } @Override protected void reset() { companyId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/company/ContactPersonResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.company; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import org.springframework.util.LinkedMultiValueMap; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; @Sql("contactPerson/resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_SUPERVISOR") public class ContactPersonResourceSecurityTest extends AbstractDomainResourceSecurityTest { private ContactPersonJsonGenerator jsonGenerator = new ContactPersonJsonGenerator(); @Override protected String getResourceName() { return "contactPersons"; } @Test @OAuthRequest public void rootAccessible() throws Exception { assertThat(root(), isAccessible()); } @Test @OAuthRequest public void one() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void postAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withCompanyId(0L).build(); assertThat(create(json), isCreated()); } @Test public void putAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withCompanyId(0L).apply(c -> c.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test @OAuthRequest public void putNotAllowedForEmployee() throws Exception { String json = jsonGenerator.start().withCompanyId(0L).apply(c -> c.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test public void patchAllowedForSupervisor() throws Exception { assertThat(updateViaPatch(0L, "{\"firstName\": \"Test\"}"), isUpdated()); } @Test @OAuthRequest public void patchNotAllowedForEmployee() throws Exception { assertThat(updateViaPatch(0L, "{\"firstName\": \"Test\"}"), isForbidden()); } @Test @OAuthRequest public void postNotAllowedForEmployee() throws Exception { String json = jsonGenerator.start().withCompanyId(0L).build(); assertThat(create(json), isForbidden()); } // @Test // public void constraintViolation() throws Exception { // mockMvc.perform( // post("/contactPersons") // .session(supervisorSession()) // .content("{}")) // .andExpect(status().isBadRequest()); // } @Test public void deleteAllowedForSupervisor() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest public void deleteForbiddenForEmployee() throws Exception { assertThat(remove(0L), isForbidden()); } @Test @OAuthRequest("ROLE_EMPLOYEE") public void updateCompanyForbiddenForEmployee() throws Exception { LinkedMultiValueMap headers = new LinkedMultiValueMap<>(); headers.put("Content-Type", asList("text/uri-list")); HttpEntity requestEntity = new HttpEntity<>("/companies/0", headers); ResponseEntity response = restTemplate.exchange(host + "/contactPersons/0/company", HttpMethod.PUT, requestEntity, String.class); assertThat(response, isForbidden()); } @Test public void updateCompanyAllowedForSupervisor() throws Exception { LinkedMultiValueMap headers = new LinkedMultiValueMap<>(); headers.put("Content-Type", asList("text/uri-list")); HttpEntity requestEntity = new HttpEntity<>("/companies/0", headers); ResponseEntity response = restTemplate.exchange(host + "/contactPersons/0/company", HttpMethod.PUT, requestEntity, String.class); assertThat(response, isNoContent()); } @Test public void deleteCompanyForbiddenForSupervisor() throws Exception { assertThat(removeUrl("/contactPersons/0/company"), isForbidden()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeControllerSecurityTest.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import de.techdev.test.rest.AbstractRestIntegrationTest; import de.techdev.trackr.domain.company.Address; import org.junit.Before; import org.junit.Test; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.math.BigDecimal; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static de.techdev.test.rest.DomainResourceTestMatchers.isForbidden; import static java.util.Collections.singletonList; import static org.junit.Assert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class EmployeeControllerSecurityTest extends AbstractRestIntegrationTest { private SelfEmployee selfEmployee; @Before public void setUp() throws Exception { selfEmployee = new SelfEmployee(); selfEmployee.setFirstName("Foo"); selfEmployee.setLastName("Bar"); selfEmployee.setId(0L); selfEmployee.setVersion(0); selfEmployee.setSalary(BigDecimal.TEN); selfEmployee.setTitle("title"); } @Test public void updateSelfViaPut() throws Exception { MultiValueMap headers = new LinkedMultiValueMap() {{ put("Content-Type", singletonList("application/json; charset=utf-8")); }}; HttpEntity request = new HttpEntity<>(generateEmployeeJson(selfEmployee), headers); ResponseEntity response = restTemplate .exchange(host + "/employees/0/self", HttpMethod.PUT, request, String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void updateOtherViaPutIsForbidden() throws Exception { MultiValueMap headers = new LinkedMultiValueMap() {{ put("Content-Type", singletonList("application/json; charset=utf-8")); }}; HttpEntity request = new HttpEntity<>(generateEmployeeJson(selfEmployee), headers); ResponseEntity response = restTemplate .exchange(host + "/employees/0/self", HttpMethod.PUT, request, String.class); assertThat(response, isForbidden()); } @Test public void accessSelf() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/employees/0/self", String.class); assertThat(response, isAccessible()); } protected String generateEmployeeJson(SelfEmployee selfEmployee) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); jg.writeStartObject() .write("firstName", selfEmployee.getFirstName()) .write("lastName", selfEmployee.getLastName()) .write("id", selfEmployee.getId()) .write("version", selfEmployee.getVersion()) .write("salary", selfEmployee.getSalary()) .write("title", selfEmployee.getTitle()); if (selfEmployee.getPhoneNumber() != null) { jg.write("phoneNumber", selfEmployee.getPhoneNumber()); } Address address = selfEmployee.getAddress(); if (address != null) { jg.writeStartObject("address") .write("id", address.getId()) .write("version", address.getVersion()) .write("street", address.getStreet()) .write("houseNumber", address.getHouseNumber()) .write("zipCode", address.getZipCode()) .write("city", address.getCity()) .write("country", address.getCountry()) .writeEnd(); } jg.writeEnd().close(); return writer.toString(); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeJsonGenerator.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.test.rest.AbstractJsonGenerator; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.util.LocalDateUtil; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.time.LocalDate; public class EmployeeJsonGenerator extends AbstractJsonGenerator { @Override protected String getJsonRepresentation(Employee employee) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); jg.writeStartObject() .write("firstName", employee.getFirstName()) .write("lastName", employee.getLastName()) .write("hourlyCostRate", employee.getHourlyCostRate()) .write("salary", employee.getSalary()) .write("email", employee.getEmail()) .write("title", employee.getTitle()) .write("federalState", employee.getFederalState().getName()); if(employee.getVacationEntitlement() != null) { jg.write("vacationEntitlement", employee.getVacationEntitlement()); } if (employee.getJoinDate() != null) { jg.write("joinDate", sdf.format(employee.getJoinDate())); } if (employee.getLeaveDate() != null) { jg.write("leaveDate", sdf.format(employee.getLeaveDate())); } if (employee.getPhoneNumber() != null) { jg.write("phoneNumber", employee.getPhoneNumber()); } if (employee.getId() != null) { jg.write("id", employee.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected Employee getNewTransientObject(int i) { Employee employee = new Employee(); employee.setFirstName("firstName_" + i); employee.setLastName("lastName_" + i); employee.setTitle("title_" + i); employee.setPhoneNumber("phoneNumber_" + i); employee.setSalary(BigDecimal.TEN); employee.setVacationEntitlement(30f); employee.setEmail("email" + i + "@techdev.de"); employee.setHourlyCostRate(new BigDecimal("85.5")); employee.setJoinDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 1))); employee.setFederalState(FederalState.BERLIN); return employee; } @Override protected EmployeeJsonGenerator getSelf() { return this; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeResourceIntegrationTest.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.test.context.jdbc.Sql; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; @Sql(scripts = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public class EmployeeResourceIntegrationTest extends AbstractRestIntegrationTest { private EmployeeJsonGenerator jsonGenerator = new EmployeeJsonGenerator(); @Autowired private SettingsRepository settingsRepository; @Test @OAuthRequest("ROLE_ADMIN") public void creatingAnEmployeeViaRestAlsoCreatesInitialLocaleSettings() throws Exception { MultiValueMap headers = new LinkedMultiValueMap() {{ put("Content-Type", singletonList("application/json; charset=utf-8")); }}; Employee employee = jsonGenerator.getNewTransientObject(500); employee.setEmail("email@techdev.de"); HttpEntity request = new HttpEntity<>(jsonGenerator.getJsonRepresentation(employee), headers); restTemplate.exchange(host + "/employees", HttpMethod.POST, request, String.class); Settings settings = settingsRepository.findByTypeAndEmployee_Email(Settings.SettingsType.LOCALE, "email@techdev.de"); assertThat(settings, is(notNullValue())); assertThat(settings.getValue(), is("de")); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.text.SimpleDateFormat; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_SUPERVISOR") public class EmployeeResourceSecurityTest extends AbstractDomainResourceSecurityTest { @Override protected String getResourceName() { return "employees"; } @Test @OAuthRequest public void rootNotAllowedForEmployees() throws Exception { assertThat(root(), isForbidden()); } @Test public void rootAllowedForSupervisors() throws Exception { assertThat(root(), isAccessible()); } @Test public void oneIsAllowedForSupervisor() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest(username = "employee@techdev.de") public void oneIsAllowedForSelf() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void oneIsForbiddenForOtherEmployee() throws Exception { assertThat(one(0L), isForbidden()); } // @Test // public void postAllowedForAdmins() throws Exception { // assertThat(create(adminSession()), isCreated()); // } // @Test // public void putAllowedForSupervisors() throws Exception { // assertThat(update(supervisorSession()), isUpdated()); // } // @Test // public void patchAllowedForSupervisors() throws Exception { // assertThat(updateViaPatch(supervisorSession(), "{\"firstName\": \"Test\"}"), isUpdated()); // } @Test @OAuthRequest("ROLE_ADMIN") public void deleteAllowedForAdmins() throws Exception { assertThat(remove(0L), isNoContent()); } // @Test // public void postForbiddenForSupervisor() throws Exception { // assertThat(create(supervisorSession()), isForbidden()); // } // @Test // public void putForbiddenForEmployee() throws Exception { // assertThat(update(employee -> employeeSession(employee.getEmail())), isForbidden()); // } @Test public void deleteForbiddenForSupervisor() throws Exception { assertThat(remove(0L), isForbidden()); } protected String getJsonRepresentation(Employee employee) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); jg.writeStartObject() .write("firstName", employee.getFirstName()) .write("lastName", employee.getLastName()) .write("hourlyCostRate", employee.getHourlyCostRate()) .write("salary", employee.getSalary()) .write("email", employee.getEmail()) .write("title", employee.getTitle()) .write("federalState", employee.getFederalState().getName()); if(employee.getVacationEntitlement() != null) { jg.write("vacationEntitlement", employee.getVacationEntitlement()); } if (employee.getJoinDate() != null) { jg.write("joinDate", sdf.format(employee.getJoinDate())); } if (employee.getLeaveDate() != null) { jg.write("leaveDate", sdf.format(employee.getLeaveDate())); } if (employee.getPhoneNumber() != null) { jg.write("phoneNumber", employee.getPhoneNumber()); } if (employee.getId() != null) { jg.write("id", employee.getId()); } jg.writeEnd().close(); return writer.toString(); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/SelfEmployeeRepositoryTest.java ================================================ package de.techdev.trackr.domain.employee; import de.techdev.trackr.domain.company.Address; import de.techdev.trackr.domain.company.AddressRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.math.BigDecimal; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class SelfEmployeeRepositoryTest { @InjectMocks private SelfEmployeeRepository selfEmployeeRepository; @Mock private EmployeeRepository employeeRepository; @Mock private AddressRepository addressRepository; @Before public void setUp() throws Exception { Employee employee = new Employee(); employee.setId(0L); employee.setTitle("oldTitle"); employee.setSalary(BigDecimal.ONE); when(employeeRepository.findOne(eq(0L))).thenReturn(employee); when(employeeRepository.save(any(Employee.class))).thenAnswer(inv -> inv.getArguments()[0]); when(addressRepository.save(any(Address.class))).thenAnswer(inv -> inv.getArguments()[0]); } @Test public void updateSelfUpdatesAllAllowedFields() throws Exception { SelfEmployee selfEmployee = new SelfEmployee(); selfEmployee.setId(0l); selfEmployee.setFirstName("firstName"); selfEmployee.setLastName("lastName"); selfEmployee.setPhoneNumber("12345"); Employee updatedEmployee = selfEmployeeRepository.save(0L, selfEmployee); assertThat(updatedEmployee.getId(), is(0l)); assertThat(updatedEmployee.getFirstName(), is("firstName")); assertThat(updatedEmployee.getLastName(), is("lastName")); assertThat(updatedEmployee.getPhoneNumber(), is("12345")); } @Test public void updateSelfDoesNotUpdateForbiddenFields() throws Exception { SelfEmployee selfEmployee = new SelfEmployee(); selfEmployee.setId(0l); selfEmployee.setSalary(BigDecimal.TEN); selfEmployee.setTitle("title"); Employee updatedEmployee = selfEmployeeRepository.save(0L, selfEmployee); assertThat(updatedEmployee.getTitle(), is("oldTitle")); assertThat(updatedEmployee.getSalary(), is(BigDecimal.ONE)); } @Test public void updateSelfUpdatesTheAddressWhenItIsNotNull() throws Exception { SelfEmployee selfEmployee = new SelfEmployee(); Address address = new Address(); selfEmployee.setAddress(address); Employee updatedEmployee = selfEmployeeRepository.save(0L, selfEmployee); verify(addressRepository).save(eq(address)); assertThat(updatedEmployee.getAddress(), is(address)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/addressbook/AddressBookControllerSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.addressbook; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static org.junit.Assert.assertThat; @OAuthRequest public class AddressBookControllerSecurityTest extends AbstractRestIntegrationTest { @Test public void rootIsAccessible() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/address_book", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/addressbook/AddressBookControllerTest.java ================================================ package de.techdev.trackr.domain.employee.addressbook; import de.techdev.trackr.domain.employee.Employee; import org.junit.Before; import org.junit.Test; import java.util.List; import static java.util.Collections.singletonList; import static org.echocat.jomon.testing.BaseMatchers.isNotEmpty; import static org.junit.Assert.assertThat; public class AddressBookControllerTest { private AddressBookController addressBookController; @Before public void setUp() throws Exception { addressBookController = new AddressBookController(); } @Test public void transformEmployees() throws Exception { Employee employee = new Employee(); List listOfEmployees = singletonList(employee); List employeeForAddressBookDTOs = addressBookController.transformToReducedEmployees(listOfEmployees); assertThat(employeeForAddressBookDTOs, isNotEmpty()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseJsonGenerator.java ================================================ package de.techdev.trackr.domain.employee.expenses; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; public class TravelExpenseJsonGenerator extends AbstractJsonGenerator { private Long reportId; public TravelExpenseJsonGenerator withReportId(Long reportId) { this.reportId = reportId; return this; } @Override protected String getJsonRepresentation(TravelExpense object) { if (reportId == null) { throw new IllegalStateException("Report id must not be null"); } StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); jg.writeStartObject() .write("cost", object.getCost()) .write("vat", object.getVat()) .write("fromDate", sdf.format(object.getFromDate())) .write("toDate", sdf.format(object.getToDate())) .write("submissionDate", sdf2.format(object.getSubmissionDate())) .write("type", object.getType().toString()) .write("paid", object.isPaid()) .write("report", "/objectReports/" + reportId); if(object.getComment() != null) { jg.write("comment", object.getComment()); } if (object.getId() != null) { jg.write("id", object.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected TravelExpense getNewTransientObject(int i) { TravelExpense travelExpense = new TravelExpense(); travelExpense.setFromDate(new Date()); travelExpense.setToDate(new Date()); travelExpense.setCost(BigDecimal.TEN); travelExpense.setType(TravelExpense.Type.TAXI); travelExpense.setSubmissionDate(new Date()); travelExpense.setVat(BigDecimal.ONE); travelExpense.setComment("comment_" + i); return travelExpense; } @Override protected TravelExpenseJsonGenerator getSelf() { return this; } @Override protected void reset() { reportId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.expenses; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class TravelExpenseResourceSecurityTest extends AbstractDomainResourceSecurityTest { private TravelExpenseJsonGenerator jsonGenerator = new TravelExpenseJsonGenerator(); @Override protected String getResourceName() { return "travelExpenses"; } @Test @OAuthRequest("ROLE_ADMIN") public void rootNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test @OAuthRequest(value = "ROLE_ADMIN", username = "admin@techdev.de") public void oneForbiddenForOther() throws Exception { assertThat(one(0L), isForbidden()); } @Test public void oneAllowedForSelf() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void createAllowedForSelf() throws Exception { String json = jsonGenerator.start().withReportId(0L).build(); assertThat(create(json), isCreated()); } @Test @Ignore @OAuthRequest(username = "someone.else@techdev.de") public void createNotAllowedForOther() throws Exception { String json = jsonGenerator.start().withReportId(0L).build(); assertThat(create(json), isForbidden()); } @Test public void updateAllowedForSelf() throws Exception { String json = jsonGenerator.start().withReportId(0L).apply(t -> t.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test public void deletePendingAllowed() throws Exception { assertThat(remove(0L), isNoContent()); } @Test public void deleteAcceptedNotAllowed() throws Exception { assertThat(remove(1L), isForbidden()); } @Test public void deleteSubmittedNotAllowed() throws Exception { assertThat(remove(2L), isForbidden()); } @Test @Ignore public void changeReportNotAllowed() throws Exception { assertThat(updateLink(0L, "report", "/travelExpenseReports/0"), isForbidden()); } @Test public void accessTypes() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/travelExpenses/types", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportJsonGenerator.java ================================================ package de.techdev.trackr.domain.employee.expenses.report; import de.techdev.test.rest.AbstractJsonGenerator; import de.techdev.trackr.domain.employee.expenses.reports.Report; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Date; public class ReportJsonGenerator extends AbstractJsonGenerator { private Long employeeId; private Long debitorId; private Long projectId; public ReportJsonGenerator withEmployeeId(Long employeeId) { this.employeeId = employeeId; return this; } public ReportJsonGenerator withDebitorId(Long debitorId) { this.debitorId = debitorId; return this; } public ReportJsonGenerator withProjectId(Long projectId) { this.projectId = projectId; return this; } @Override protected String getJsonRepresentation(Report report) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); jg.writeStartObject() .write("version", report.getVersion()) .write("status", report.getStatus().toString()); if (employeeId != null) { jg.write("employee", "/api/employees/" + employeeId); } if (debitorId != null) { jg.write("debitor", "/api/companies/" + debitorId); } if (projectId != null) { jg.write("project", "/api/projects/" + projectId); } if (report.getSubmissionDate() != null) { jg.write("submissionDate", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(report.getSubmissionDate())); } if (report.getId() != null) { jg.write("id", report.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected Report getNewTransientObject(int i) { Report report = new Report(); report.setVersion(0); report.setStatus(Report.Status.PENDING); report.setSubmissionDate(new Date()); return report; } @Override protected ReportJsonGenerator getSelf() { return this; } @Override protected void reset() { employeeId = null; debitorId = null; projectId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.expenses.report; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class ReportResourceSecurityTest extends AbstractDomainResourceSecurityTest { private ReportJsonGenerator jsonGenerator = new ReportJsonGenerator(); @Override protected String getResourceName() { return "travelExpenseReports"; } @Test @OAuthRequest("ROLE_ADMIN") public void rootNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void oneAllowedForSupervisor() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void oneNotAllowedForOther() throws Exception { assertThat(one(0L), isForbidden()); } @Test public void oneAllowedForSelf() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void createAllowed() throws Exception { String json = jsonGenerator.start().withDebitorId(0L).withEmployeeId(0L).build(); assertThat(create(json), isCreated()); } @Test public void updateNotAllowedForSelf() throws Exception { String json = jsonGenerator.start().withDebitorId(0L).withEmployeeId(0L).apply(r -> r.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test @Ignore("yields 400 instead of 403 ?") @OAuthRequest(username = "someone.else@techdev.de") public void updateForbiddenForOther() throws Exception { String json = jsonGenerator.start().withDebitorId(0L).withEmployeeId(0L).apply(r -> r.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test public void deleteAllowedForOwnerIfPending() throws Exception { assertThat(remove(0L), isNoContent()); } @Test public void deleteForbiddenForOwnerIfSubmitted() throws Exception { assertThat(remove(1L), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void deleteAllowedForAdmin() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void deleteForbiddenForOtherEvenIfPending() throws Exception { assertThat(remove(0L), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void pdfExport() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/travelExpenseReports/0/pdf", byte[].class); assertThat(response, isAccessible()); } @Test public void pdfExportAsEmployee() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/travelExpenseReports/0/pdf", byte[].class); assertThat(response, isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void updateEmployeeNotAllowedForSupervisor() throws Exception { assertThat(updateLink(0L, "employee", "/employees/0"), isForbidden()); } @Test // TODO: Not a really useful test as expenses are never added this way (they need to have the report first...) public void addTravelExpenseAllowedForSelf() throws Exception { assertThat(updateLink(0L, "expenses", "/travelExpenses/0"), isNoContent()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void addTravelExpenseNotAllowedForOther() throws Exception { assertThat(updateLink(0L, "expenses", "/travelExpenses/0"), isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "someone.else@techdev.de") public void submitNotAllowedForOtherSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/travelExpenseReports/0/submit", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR") public void approveNotAllowedForOwningSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/travelExpenseReports/0/approve", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "someone.else@techdev.de") public void approveAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/travelExpenseReports/0/approve", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isNoContent()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "someone.else@techdev.de") public void rejectAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/travelExpenseReports/0/reject", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByStatusAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/travelExpenseReports/search/findByStatus?status=SUBMITTED", String.class); assertThat(response, isAccessible()); } @Test public void findByStatusNotAllowedForEmployee() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/travelExpenseReports/search/findByStatus?status=SUBMITTED", String.class); assertThat(response, isForbidden()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportServiceTest.java ================================================ package de.techdev.trackr.domain.employee.expenses.report; import de.techdev.trackr.domain.employee.EmployeeRepository; import de.techdev.trackr.domain.employee.expenses.reports.Report; import de.techdev.trackr.domain.employee.expenses.reports.ReportNotifyService; import de.techdev.trackr.domain.employee.expenses.reports.ReportRepository; import de.techdev.trackr.domain.employee.expenses.reports.ReportService; import de.techdev.trackr.util.LocalDateUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.time.LocalDate; import java.util.Date; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class ReportServiceTest { @InjectMocks private ReportService service; @Mock private ReportRepository reportRepository; @Mock private EmployeeRepository employeeRepository; @Mock private ReportNotifyService reportNotifyService; @Before public void setUp() throws Exception { when(reportRepository.save(any(Report.class))).then(invocation -> invocation.getArguments()[0]); } @Test public void testReject() throws Exception { Report travelExpenseReport = new Report(); travelExpenseReport.setStatus(Report.Status.SUBMITTED); Report result = service.reject(travelExpenseReport, "admin@techdev.de"); assertThat(result.getStatus(), is(Report.Status.REJECTED)); } @Test public void testApprove() throws Exception { Report travelExpenseReport = new Report(); travelExpenseReport.setStatus(Report.Status.SUBMITTED); Report result = service.accept(travelExpenseReport, "admin@techdev.de"); assertThat(result.getStatus(), is(Report.Status.APPROVED)); } @Test public void testSubmit() throws Exception { Report travelExpenseReport = new Report(); LocalDate localDate = LocalDate.of(2014, 1, 1); Date date = LocalDateUtil.fromLocalDate(localDate); travelExpenseReport.setStatus(Report.Status.PENDING); travelExpenseReport.setSubmissionDate(date); Report result = service.submit(travelExpenseReport); assertThat(result.getStatus(), is(Report.Status.SUBMITTED)); assertThat(result.getSubmissionDate().after(date), is(true)); verify(reportNotifyService, times(1)).notifySupervisorsOnSubmission(eq(travelExpenseReport)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/comment/CommentJsonGenerator.java ================================================ package de.techdev.trackr.domain.employee.expenses.report.comment; import de.techdev.test.rest.AbstractJsonGenerator; import de.techdev.trackr.domain.employee.expenses.reports.comments.Comment; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Date; public class CommentJsonGenerator extends AbstractJsonGenerator { private Long reportId; private Long employeeId; @Override protected String getJsonRepresentation(Comment object) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); jg.writeStartObject() .write("text", object.getText()) .write("employee", "/employees/" + employeeId) .write("submissionDate", sdf.format(object.getSubmissionDate())) .write("travelExpenseReport", "/travelExpenseReports/" + reportId); if (object.getId() != null) { jg.write("id", object.getId()); } jg.writeEnd().close(); return writer.toString(); } public CommentJsonGenerator withReportId(Long reportId) { this.reportId = reportId; return this; } public CommentJsonGenerator withEmployeeId(Long employeeId) { this.employeeId = employeeId; return this; } @Override protected Comment getNewTransientObject(int i) { Comment comment = new Comment(); comment.setSubmissionDate(new Date()); comment.setText("text_" + i); return comment; } @Override protected CommentJsonGenerator getSelf() { return this; } @Override protected void reset() { reportId = null; employeeId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/comment/CommentResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.expenses.report.comment; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class CommentResourceSecurityTest extends AbstractDomainResourceSecurityTest { private CommentJsonGenerator jsonGenerator = new CommentJsonGenerator(); @Override protected String getResourceName() { return "travelExpenseReportComments"; } @Test public void rootNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test public void oneNotExported() throws Exception { assertThat(one(0L), isMethodNotAllowed()); } @Test public void createAllowedForOwningEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withReportId(0L).build(); assertThat(create(json), isCreated()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void createAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withReportId(0L).build(); assertThat(create(json), isCreated()); } @Test @OAuthRequest(value = "ROLE_ADMIN") public void updateForbidden() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withReportId(0L).apply(c -> c.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test @OAuthRequest(value = "ROLE_ADMIN") public void deleteNotExported() throws Exception { assertThat(remove(0L), isMethodNotAllowed()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/login/PrincipalControllerSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.login; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static org.junit.Assert.assertThat; @OAuthRequest @Sql("resourceTest.sql") @Sql(value = "/de/techdev/trackr/domain/emptyDatabase.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public class PrincipalControllerSecurityTest extends AbstractRestIntegrationTest { @Test public void principal() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/principal", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/sickdays/SickDaysJsonGenerator.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import de.techdev.test.rest.AbstractJsonGenerator; import de.techdev.trackr.util.LocalDateUtil; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.time.LocalDate; public class SickDaysJsonGenerator extends AbstractJsonGenerator { private Long employeeId; public SickDaysJsonGenerator withEmployeeId(Long employeeId) { this.employeeId = employeeId; return this; } @Override protected String getJsonRepresentation(SickDays object) { if (employeeId == null) { throw new IllegalStateException("employee id must not be null"); } StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); jg.writeStartObject() .write("startDate", sdf.format(object.getStartDate())) .write("employee", "/api/employees/" + employeeId); if (object.getEndDate() != null) { jg.write("endDate", sdf.format(object.getEndDate())); } if (object.getId() != null) { jg.write("id", object.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected SickDays getNewTransientObject(int i) { SickDays sickDays = new SickDays(); LocalDate now = LocalDate.now(); sickDays.setStartDate(LocalDateUtil.fromLocalDate(now)); if (i % 2 == 0) { sickDays.setEndDate(LocalDateUtil.fromLocalDate(now.plusDays(i))); } return sickDays; } @Override protected SickDaysJsonGenerator getSelf() { return this; } @Override protected void reset() { employeeId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/sickdays/SickDaysResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.sickdays; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.echocat.jomon.testing.Assert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class SickDaysResourceSecurityTest extends AbstractDomainResourceSecurityTest { private SickDaysJsonGenerator jsonGenerator = new SickDaysJsonGenerator(); @Override protected String getResourceName() { return "sickDays"; } @Test @OAuthRequest("ROLE_SUPERVISOR") public void rootIsNotAccessibleForAdmin() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test public void oneIsAllowedForEmployee() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void oneIsForbiddenForOther() throws Exception { assertThat(one(0L), isForbidden()); } @Test public void createIsAllowedForEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).build(); assertThat(create(json), isCreated()); } @Test @OAuthRequest(username = "someone.else@techdev.de") @Ignore // Not testable at the moment since it returns 400 instead of 403 (it can't convert the JSON because the employee is also restricted). public void createIsForbiddenForOther() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).build(); assertThat(create(json), isCreated()); } @Test @OAuthRequest("ROLE_ADMIN") public void deleteIsAllowedForAdmin() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteIsForbiddenForSupervisor() throws Exception { assertThat(remove(0L), isForbidden()); } @Test public void updateIsAllowedForEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).apply(s -> s.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test @Ignore @OAuthRequest(username = "someone.else@techdev.de") // Not testable at the moment since it returns 400 instead of 403 (it can't convert the JSON because the employee is also restricted). public void updateIsForbiddenForOther() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).apply(s -> s.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test public void findByEmployeeIsAllowedForEmployee() throws Exception { assertThat(oneUrl("/sickDays/search/findByEmployee?employee=/employees/0"), isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") @Ignore // Not testable at the moment since it returns 400 instead of 403 (it can't convert the JSON because the employee is also restricted). public void findByEmployeeIsForbiddenForOther() throws Exception { assertThat(oneUrl("/sickDays/search/findByEmployee?employee=0"), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void findByStartDateBetweenOrEndDateBetweenIsAllowedForAdmin() throws Exception { assertThat( oneUrl("/sickDays/search/findByStartDateBetweenOrEndDateBetween?startLower=2014-07-01&startHigher=2014-07-31&endLower=2014-07-08&endHigher=2014-08-09"), isAccessible() ); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByStartDateBetweenOrEndDateBetweenIsForbiddenForSupervisor() throws Exception { assertThat( oneUrl("/sickDays/search/findByStartDateBetweenOrEndDateBetween?startLower=2014-07-01&startHigher=2014-07-31&endLower=2014-07-08&endHigher=2014-08-09"), isForbidden() ); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/HolidayCalculatorTest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import org.junit.Before; import org.junit.Test; import java.time.LocalDate; import java.time.Month; import java.util.List; import static java.util.Arrays.asList; import static org.echocat.jomon.testing.BaseMatchers.isTrue; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; /** * @author Moritz Schulze */ public class HolidayCalculatorTest { private HolidayCalculator holidayCalculator; @Before public void setUp() throws Exception { holidayCalculator = new HolidayCalculator(); } @Test public void calculateDifferenceBetweenExcludingHolidaysAndWeekends() throws Exception { LocalDate start = LocalDate.of(2014, Month.DECEMBER, 19); LocalDate end = LocalDate.of(2014, Month.DECEMBER, 31); Integer numberOfDays = holidayCalculator.calculateDifferenceBetweenExcludingHolidaysAndWeekends(start, end, getHolidaysAsDates()); assertThat(numberOfDays, is(7)); } @Test public void calculateDifferenceBetweenExcludingHolidaysAndWeekendsAgain() throws Exception { LocalDate start = LocalDate.of(2014, Month.MARCH, 4); LocalDate end = LocalDate.of(2014, Month.MARCH, 10); Integer numberOfDays = holidayCalculator.calculateDifferenceBetweenExcludingHolidaysAndWeekends(start, end, getHolidaysAsDates()); assertThat(numberOfDays, is(5)); } @Test public void calculateDifferenceBetweenExcludingHolidaysAndWeekendsLastDayWeekend() throws Exception { LocalDate start = LocalDate.of(2014, Month.MARCH, 10); LocalDate end = LocalDate.of(2014, Month.MARCH, 16); Integer numberOfDays = holidayCalculator.calculateDifferenceBetweenExcludingHolidaysAndWeekends(start, end, getHolidaysAsDates()); assertThat(numberOfDays, is(5)); } @Test public void isWeekendOrHolidaySaturday() throws Exception { LocalDate date = LocalDate.of(2014, Month.MARCH, 15); boolean test = holidayCalculator.isWeekendOrHoliday(date, null); assertThat(test, isTrue()); } @Test public void isWeekendOrHolidaySunday() throws Exception { LocalDate date = LocalDate.of(2014, Month.MARCH, 16); boolean test = holidayCalculator.isWeekendOrHoliday(date, null); assertThat(test, isTrue()); } @Test public void isWeekendOrHolidayInList() throws Exception { LocalDate date = LocalDate.of(2014, Month.DECEMBER, 25); boolean test = holidayCalculator.isWeekendOrHoliday(date, getHolidaysAsDates()); assertThat(test, isTrue()); } private List getHolidaysAsDates() { LocalDate holiday1 = LocalDate.of(2014, Month.DECEMBER, 25); LocalDate holiday2 = LocalDate.of(2014, Month.DECEMBER, 26); LocalDate holiday3 = LocalDate.of(2014, Month.OCTOBER, 3); return asList(holiday1, holiday2, holiday3); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/HolidayResourceTest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static de.techdev.test.rest.DomainResourceTestMatchers.isMethodNotAllowed; import static org.junit.Assert.assertThat; @Sql("holiday/resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class HolidayResourceTest extends AbstractDomainResourceSecurityTest { @Override protected String getResourceName() { return "holidays"; } @Test public void rootAccessible() throws Exception { assertThat(root(), isAccessible()); } @Test public void oneAccessible() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void createNotExported() throws Exception { assertThat(create("{}"), isMethodNotAllowed()); } @Test public void updateNotExported() throws Exception { assertThat(update(0L, "{}"), isMethodNotAllowed()); } @Test public void deleteNotExported() throws Exception { assertThat(remove(0L), isMethodNotAllowed()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestControllerSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.test.TestConstants; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static de.techdev.test.rest.DomainResourceTestMatchers.isForbidden; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @Sql("resourceTest.sql") @Sql(TestConstants.CREATE_UUID_MAPPING_TABLE_SQL_FILE) @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_SUPERVISOR") public class VacationRequestControllerSecurityTest extends AbstractRestIntegrationTest { @Test @OAuthRequest("ROLE_SUPERVISOR") public void approveNotAllowedForSupervisorOnOwnVacationRequest() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/vacationRequests/0/approve", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isForbidden()); ResponseEntity vacationRequest = restTemplate.getForEntity(host + "/vacationRequests/0", VacationRequest.class); assertThat(vacationRequest.getBody().getStatus(), is(VacationRequest.VacationRequestStatus.PENDING)); } @Test @OAuthRequest public void approveNotAllowedForEmployees() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/vacationRequests/0/approve", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isForbidden()); ResponseEntity vacationRequest = restTemplate.getForEntity(host + "/vacationRequests/0", VacationRequest.class); assertThat(vacationRequest.getBody().getStatus(), is(VacationRequest.VacationRequestStatus.PENDING)); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void approveAllowedForOtherSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/vacationRequests/0/approve", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isAccessible()); ResponseEntity vacationRequest = restTemplate.getForEntity(host + "/vacationRequests/0", VacationRequest.class); assertThat(vacationRequest.getBody().getStatus(), is(VacationRequest.VacationRequestStatus.APPROVED)); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void rejectAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/vacationRequests/0/reject", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isAccessible()); ResponseEntity vacationRequest = restTemplate.getForEntity(host + "/vacationRequests/0", VacationRequest.class); assertThat(vacationRequest.getBody().getStatus(), is(VacationRequest.VacationRequestStatus.REJECTED)); } @Test public void selfRejectForbiddenForSupervisor() throws Exception { ResponseEntity response = restTemplate.exchange(host + "/vacationRequests/0/reject", HttpMethod.PUT, HttpEntity.EMPTY, String.class); assertThat(response, isForbidden()); ResponseEntity vacationRequest = restTemplate.getForEntity(host + "/vacationRequests/0", VacationRequest.class); assertThat(vacationRequest.getBody().getStatus(), is(VacationRequest.VacationRequestStatus.PENDING)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestJsonGenerator.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Date; public class VacationRequestJsonGenerator extends AbstractJsonGenerator { private Long employeeId; private Long approverId; public VacationRequestJsonGenerator withEmployeeId(Long employeeId) { this.employeeId = employeeId; return this; } public VacationRequestJsonGenerator withApproverId(Long approverId) { this.approverId = approverId; return this; } @Override protected String getJsonRepresentation(VacationRequest object) { if (employeeId == null) { throw new IllegalStateException("Employee id must not be null"); } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); StringWriter writer = new StringWriter(); JsonGenerator jsonGenerator = jsonGeneratorFactory.createGenerator(writer); JsonGenerator jg = jsonGenerator .writeStartObject() .write("startDate", sdf.format(object.getStartDate())) .write("endDate", sdf.format(object.getEndDate())) .write("status", object.getStatus().toString()) .write("employee", "/api/employees/" + employeeId); if (object.getId() != null) { jg.write("id", object.getId()); } if (approverId != null) { jsonGenerator.write("approver", "/api/employees/" + approverId); } if (object.getApprovalDate() != null) { jg.write("approvalDate", sdf2.format(object.getApprovalDate())); } if (object.getNumberOfDays() != null) { jg.write("numberOfDays", object.getNumberOfDays()); } if (object.getSubmissionTime() != null) { jg.write("submissionTime", sdf2.format(object.getSubmissionTime())); } jg.writeEnd().close(); return writer.toString(); } @Override protected VacationRequest getNewTransientObject(int i) { VacationRequest vacationRequest = new VacationRequest(); vacationRequest.setStatus(VacationRequest.VacationRequestStatus.PENDING); vacationRequest.setStartDate(new Date()); vacationRequest.setEndDate(new Date()); return vacationRequest; } @Override protected VacationRequestJsonGenerator getSelf() { return this; } @Override protected void reset() { employeeId = null; approverId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestRepositoryTest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.test.TransactionalIntegrationTest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import de.techdev.trackr.util.LocalDateUtil; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.jdbc.Sql; import java.time.LocalDate; import java.util.Date; import java.util.List; import static org.echocat.jomon.testing.BaseMatchers.hasSize; import static org.echocat.jomon.testing.BaseMatchers.isNotEmpty; import static org.hamcrest.MatcherAssert.assertThat; @Sql("repositoryTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public class VacationRequestRepositoryTest extends TransactionalIntegrationTest { @Autowired private VacationRequestRepository vacationRequestRepository; @Test public void findBySubmissionTimeBefore() throws Exception { List all = vacationRequestRepository .findBySubmissionTimeBeforeAndStatus(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 10, 16)), VacationRequest.VacationRequestStatus.PENDING); assertThat(all, isNotEmpty()); } @Test public void findByApprovedBetween() { Date start = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 10, 1)); Date end = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 12, 8)); List all = vacationRequestRepository .findByStartDateBetweenOrEndDateBetweenAndStatus(start, end, start, end, VacationRequest.VacationRequestStatus.APPROVED); assertThat(all, hasSize(1)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import de.techdev.test.TestConstants; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(TestConstants.CREATE_UUID_MAPPING_TABLE_SQL_FILE) @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class VacationRequestResourceSecurityTest extends AbstractDomainResourceSecurityTest { private VacationRequestJsonGenerator jsonGenerator = new VacationRequestJsonGenerator(); @Override protected String getResourceName() { return "vacationRequests"; } @Test public void rootNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test public void oneAllowedForEmployee() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void oneForbiddenForOther() throws Exception { assertThat(one(0L), isForbidden()); } @Test public void findByEmployeeOrderByStartDateAscAllowedForEmployee() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/search/findByEmployeeOrderByStartDateAsc?employee=/employees/0", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void findByEmployeeOrderByStartDateAscAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/search/findByEmployeeOrderByStartDateAsc?employee=/employees/0", String.class); assertThat(response, isAccessible()); } /** * Access is forbidden, but currently spring-data-rest will throw a 400 because the employee cannot be unmarshalled from the id. */ @Test @Ignore @OAuthRequest(username = "someone.else@techdev.de") public void findByEmployeeOrderByStartDateAscForbiddenForOther() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/search/findByEmployeeOrderByStartDateAsc?employee=0", String.class); assertThat(response, isForbidden()); } @Test public void createAllowedForEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).build(); assertThat(create(json), isCreated()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void createForbiddenForSupervisorIfNotOwner() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).build(); assertThat(create(json), isForbidden()); } @Test public void updateForbiddenForEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).apply(v -> v.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void updateAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).apply(v -> v.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void updateSelfNotAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).apply(v -> v.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } /** * Access is forbidden, but currently spring-data-rest will throw a 400 because the employee cannot be unmarshalled from the id. * * @throws Exception */ @Test @Ignore @OAuthRequest(username = "someone.else@techdev.de") public void updateForbiddenForOther() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).apply(v -> v.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test public void deleteAllowedForEmployee() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest("employee2@techdev.de") public void deleteApprovedNotAllowedForEmployee() throws Exception { assertThat(remove(1L), isForbidden()); } @Test @OAuthRequest("employee2@techdev.de") public void deleteRejectedNotAllowedForEmployee() throws Exception { assertThat(remove(2L), isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void deleteAllowedForSupervisor() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteForbiddenForOwningSupervisor() throws Exception { assertThat(remove(1L), isForbidden()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void deleteForbiddenForOther() throws Exception { assertThat(remove(0L), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void updateEmployeeIsForbidden() throws Exception { assertThat(updateLink(0L, "employee", "/employees/0"), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void updateApproverIsForbidden() throws Exception { assertThat(updateLink(0L, "approver", "/employees/0"), isForbidden()); } @Test public void deleteEmployeeIsForbidden() throws Exception { assertThat(removeUrl("/vacationRequests/0/employee"), isForbidden()); } @Test public void deleteApproverIsForbidden() throws Exception { assertThat(removeUrl("/vacationRequests/1/approver"), isForbidden()); } @Test @Ignore("See DATAREST-476") public void getApprover() throws Exception { assertThat(oneUrl("/vacationRequests/1/approver"), isAccessible()); } @Test public void findByStatusOrderBySubmissionTimeAscForbiddenForEmployee() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/search/findByStatusOrderBySubmissionTimeAsc?approved=APPROVED", String.class); assertThat(response, isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByStatusOrderBySubmissionTimeAscAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/search/findByStatusOrderBySubmissionTimeAsc?approved=APPROVED", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest("ROLE_ADMIN") public void daysPerEmployeeBetweenAccessibleForAdmin() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/daysPerEmployeeBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void daysPerEmployeeBetweenForbiddenForSupervisor() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/vacationRequests/daysPerEmployeeBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestScheduledJobsTest.java ================================================ package de.techdev.trackr.domain.employee.vacation; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class VacationRequestScheduledJobsTest { @Mock private VacationRequestApproveService vacationRequestApproveService; @InjectMocks private VacationRequestScheduledJobs vacationRequestScheduledJobs; @Test public void callsTheRightMethod() throws Exception { doNothing().when(vacationRequestApproveService).approveSevenDayOldRequests(); vacationRequestScheduledJobs.approveSevenDaysOldVacationRequests(); verify(vacationRequestApproveService, times(1)).approveSevenDayOldRequests(); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/MailApproveServiceTest.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import org.junit.Before; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; public class MailApproveServiceTest { private MailApproveService mailApproveService; @Before public void setUp() throws Exception { mailApproveService = new MailApproveService(); } @Test public void approveOrReject_approve() throws Exception { VacationRequest.VacationRequestStatus status = mailApproveService.approveOrReject("approve\nyou can answer approve or reject"); assertThat(status, is(VacationRequest.VacationRequestStatus.APPROVED)); } @Test public void approveOrReject_reject() throws Exception { VacationRequest.VacationRequestStatus status = mailApproveService.approveOrReject("reject\nyou can answer approve or reject"); assertThat(status, is(VacationRequest.VacationRequestStatus.REJECTED)); } @Test(expected = IllegalStateException.class) public void approveOrReject_exception() throws Exception { mailApproveService.approveOrReject("you can answer approve or reject"); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/MessageWrapperTest.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import org.junit.Test; import javax.mail.Session; import javax.mail.internet.*; import java.util.Properties; import static org.echocat.jomon.testing.BaseMatchers.isNull; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; public class MessageWrapperTest { @Test public void getSender() throws Exception { MimeMessage mail = new MimeMessage(Session.getDefaultInstance(new Properties())); mail.setFrom(new InternetAddress("test@techdev.de")); String sender = new MessageWrapper(mail).getSender(); assertThat(sender, is("test@techdev.de")); } @Test public void getSenderReturnsNullWhenNotTechdev() throws Exception { MimeMessage mail = new MimeMessage(Session.getDefaultInstance(new Properties())); mail.setFrom(new InternetAddress("test@gmail.de")); String sender = new MessageWrapper(mail).getSender(); assertThat(sender, isNull()); } @Test public void getBodyPlaintext() throws Exception { MimeMessage mail = new MimeMessage(Session.getDefaultInstance(new Properties())); mail.setContent("Test", "text/plain"); String body = new MessageWrapper(mail).extractContentAsString(); assertThat(body, is("Test")); } @Test public void getBodyMimeMultipart() throws Exception { MimeMessage mail = new MimeMessage(Session.getDefaultInstance(new Properties())); MimeMultipart mimeMultipart = new MimeMultipart(); MimeBodyPart plainTextPart = new MimeBodyPart(new InternetHeaders(), "Test".getBytes()); plainTextPart.setContent("Test", "text/plain"); mimeMultipart.addBodyPart(plainTextPart); MimeBodyPart otherPart = new MimeBodyPart(new InternetHeaders(), "Other".getBytes()); otherPart.setContent("Other", "text/html"); mimeMultipart.addBodyPart(otherPart); mail.setContent(mimeMultipart); String body = new MessageWrapper(mail).extractContentAsString(); assertThat(body, is("Test")); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestEmployeeToDaysTotalServiceTest.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import de.techdev.trackr.util.LocalDateUtil; import org.junit.Before; import org.junit.Test; import java.time.LocalDate; import java.util.Date; import java.util.Map; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class VacationRequestEmployeeToDaysTotalServiceTest { VacationRequestEmployeeToDaysTotalService service; @Before public void setUp() throws Exception { service = new VacationRequestEmployeeToDaysTotalService(); } @Test public void testGetMinimum() throws Exception { Date date1 = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 7, 1)); Date date2 = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 7, 2)); Date minimum = service.getMinimum(date1, date2); assertThat(minimum, is(date1)); } @Test public void testGetMaximum() throws Exception { Date date1 = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 7, 1)); Date date2 = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 7, 2)); Date maximum = service.getMaximum(date1, date2); assertThat(maximum, is(date2)); } @Test public void mapEmployeesAndSumUp() throws Exception { Employee empl1 = new Employee(); empl1.setFirstName("first_name_1"); empl1.setLastName("last_name_1"); Employee empl2 = new Employee(); empl2.setFirstName("first_name_2"); empl2.setLastName("last_name_2"); VacationRequest vr1 = new VacationRequest(); vr1.setEmployee(empl1); VacationRequest vr2 = new VacationRequest(); vr2.setEmployee(empl1); VacationRequest vr3 = new VacationRequest(); vr3.setEmployee(empl2); Map employeeDayMapping = service.mapToEmployeesAndSumUp(asList(vr1, vr2, vr3), vr -> 1); assertThat(employeeDayMapping.keySet().size(), is(2)); assertThat(employeeDayMapping.get("first_name_1 last_name_1"), is(2)); assertThat(employeeDayMapping.get("first_name_2 last_name_2"), is(1)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestNotifyServiceTest.java ================================================ package de.techdev.trackr.domain.employee.vacation.support; import de.techdev.trackr.core.mail.MailService; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.vacation.VacationRequest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.mail.SimpleMailMessage; import static org.echocat.jomon.testing.StringMatchers.contains; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class VacationRequestNotifyServiceTest { @InjectMocks private VacationRequestNotifyService vacationRequestNotifyService; @Mock private MailService mailService; private VacationRequest vacationRequest; @Before public void setUp() throws Exception { vacationRequest = new VacationRequest(); Employee employee = new Employee(); employee.setEmail("employee@techdev.de"); vacationRequest.setEmployee(employee); } @Test public void sendNotificationApproved() throws Exception { ArgumentCaptor mail = ArgumentCaptor.forClass(SimpleMailMessage.class); vacationRequest.setStatus(VacationRequest.VacationRequestStatus.APPROVED); vacationRequestNotifyService.sendEmailNotification(vacationRequest); verify(mailService, times(1)).sendMail(mail.capture()); assertThat(mail.getValue().getSubject(), contains("approved")); } @Test public void sendNotificationRejected() throws Exception { ArgumentCaptor mail = ArgumentCaptor.forClass(SimpleMailMessage.class); vacationRequest.setStatus(VacationRequest.VacationRequestStatus.REJECTED); vacationRequestNotifyService.sendEmailNotification(vacationRequest); verify(mailService, times(1)).sendMail(mail.capture()); assertThat(mail.getValue().getSubject(), contains("rejected")); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/employee/worktimetracking/WorkTimeTrackingReminderServiceIntegrationTest.java ================================================ package de.techdev.trackr.domain.employee.worktimetracking; import de.techdev.test.TransactionalIntegrationTest; import de.techdev.trackr.domain.ApiBeansConfiguration; import de.techdev.trackr.domain.common.FederalState; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; @ContextConfiguration(classes = {ApiBeansConfiguration.class}) public class WorkTimeTrackingReminderServiceIntegrationTest extends TransactionalIntegrationTest { @Autowired private WorkTimeTrackingReminderService workTimeTrackingReminderService; @Test public void remindEmployeesToTrackWorkTimes() throws Exception { workTimeTrackingReminderService.remindEmployeesToTrackWorkTimes(FederalState.BERLIN); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/ProjectJsonGenerator.java ================================================ package de.techdev.trackr.domain.project; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.math.BigDecimal; public class ProjectJsonGenerator extends AbstractJsonGenerator { private Long companyId; private Long debitorId; public ProjectJsonGenerator withCompanyId(Long companyId) { this.companyId = companyId; return this; } public ProjectJsonGenerator withDebitorId(Long debitorId) { this.debitorId = debitorId; return this; } @Override protected Project getNewTransientObject(int i) { Project project = new Project(); project.setVersion(0); project.setIdentifier("identifier_" + i); project.setName("name_" + i); project.setDailyRate(BigDecimal.TEN.multiply(new BigDecimal(i))); project.setFixedPrice(BigDecimal.TEN.multiply(new BigDecimal(i))); project.setHourlyRate(BigDecimal.TEN.multiply(new BigDecimal(i))); project.setVolume(i); return project; } @Override protected void reset() { companyId = null; debitorId = null; } @Override protected String getJsonRepresentation(Project project) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); jg.writeStartObject() .write("name", project.getName()) .write("version", project.getVersion()) .write("identifier", project.getIdentifier()) .write("volume", project.getVolume()) .write("hourlyCostRate", project.getDailyRate()) .write("salary", project.getHourlyRate()) .write("title", project.getFixedPrice()); if (debitorId != null) { jg.write("company", "/api/companies/" + companyId); } if (companyId != null) { jg.write("debitor", "/api/companies/" + debitorId); } if (project.getId() != null) { jg.write("id", project.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected ProjectJsonGenerator getSelf() { return this; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/ProjectResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.project; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import java.math.BigDecimal; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_ADMIN") public class ProjectResourceSecurityTest extends AbstractDomainResourceSecurityTest { private ProjectJsonGenerator jsonGenerator = new ProjectJsonGenerator(); @Override protected String getResourceName() { return "projects"; } @Test @OAuthRequest public void rootAccessible() throws Exception { assertThat(root(), isAccessible()); } @Test @OAuthRequest public void one() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void createAllowedForAdmin() throws Exception { String json = jsonGenerator.start().build(); assertThat(create(json), isCreated()); } @Test public void updateAllowedForAdmin() throws Exception { String json = jsonGenerator.start().apply(p -> p.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test public void deleteAllowedForAdmin() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void createForbiddenForSupervisor() throws Exception { String json = jsonGenerator.start().build(); assertThat(create(json), isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR") public void updateForbiddenForSupervisor() throws Exception { String json = jsonGenerator.start().apply(p -> p.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteForbiddenForSupervisor() throws Exception { assertThat(remove(0L), isForbidden()); } @Test public void setCompanyAllowedForAdmin() throws Exception { assertThat(updateLink(0L, "company", "/companies/0"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void setCompanyForbiddenForSupervisor() throws Exception { assertThat(updateLink(0L, "company", "/companies/0"), isForbidden()); } @Test public void setDebitorAllowedForAdmin() throws Exception { assertThat(updateLink(0L, "debitor", "/companies/0"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void setDebitorForbiddenForSupervisor() throws Exception { assertThat(updateLink(0L, "debitor", "/companies/0"), isForbidden()); } @Test public void setWorktimesAllowedForAdmin() throws Exception { assertThat(updateLink(0L, "workTimes", "/workTimes/0"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void setWorktimesForbiddenForSupervisor() throws Exception { assertThat(updateLink(0L, "workTimes", "/workTimes/0"), isForbidden()); } @Test public void deleteCompanyAllowedForAdmin() throws Exception { assertThat(removeUrl("/projects/0/company"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteCompanyForbiddenForSupervisor() throws Exception { assertThat(removeUrl("/projects/0/company"), isForbidden()); } @Test public void deleteDebitorAllowedForAdmin() throws Exception { assertThat(removeUrl("/projects/0/debitor"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteDebitorForbiddenForSupervisor() throws Exception { assertThat(removeUrl("/projects/0/debitor"), isForbidden()); } @Test public void deleteWorktimesAllowedForAdmin() throws Exception { assertThat(removeUrl("/projects/0/workTimes/0"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteWorktimesForbiddenForSupervisor() throws Exception { assertThat(removeUrl("/projects/0/workTimes/0"), isForbidden()); } public Project getNewTransientObject(int i) { Project project = new Project(); project.setIdentifier("identifier_" + i); project.setName("name_" + i); project.setDailyRate(BigDecimal.TEN.multiply(new BigDecimal(i))); project.setFixedPrice(BigDecimal.TEN.multiply(new BigDecimal(i))); project.setHourlyRate(BigDecimal.TEN.multiply(new BigDecimal(i))); project.setVolume(i); return project; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/billtimes/BillableTimeControllerIntegrationTest.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static de.techdev.test.rest.DomainResourceTestMatchers.isForbidden; import static org.junit.Assert.assertThat; public class BillableTimeControllerIntegrationTest extends AbstractRestIntegrationTest { @Test @OAuthRequest public void findEmployeeMappingByProjectAndDateBetweenForbiddenForEmployee() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/billableTimes/findEmployeeMappingByProjectAndDateBetween?project=0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findEmployeeMappingByProjectAndDateBetweenAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/billableTimes/findEmployeeMappingByProjectAndDateBetween?project=0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/billtimes/BillableTimeResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class BillableTimeResourceSecurityTest extends AbstractDomainResourceSecurityTest { private BillableTimesJsonGenerator jsonGenerator = new BillableTimesJsonGenerator(); @Override protected String getResourceName() { return "billableTimes"; } @Test public void rootNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void oneAllowedForSupervisor() throws Exception { assertThat(one(0L), isAccessible()); } @Test public void oneForbiddenForEmployee() throws Exception { assertThat(one(0L), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void createAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).build(); assertThat(create(json), isCreated()); } @Test public void createForbiddenForEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).build(); assertThat(create(json), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void deleteAllowedForSupervisor() throws Exception { assertThat(remove(0L), isNoContent()); } @Test public void deleteForbiddenForEmployee() throws Exception { assertThat(remove(0L), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void updateAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).apply(b -> b.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } /** * TODO: this fails with HTTP 400 because findOne is annotated with @PreAuthorize and spring-data-rest doesn't forward the AccessDeniedException correctly */ @Test @Ignore public void updateForbiddenForEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).apply(b -> b.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void deleteEmployeeForbidden() throws Exception { assertThat(removeUrl("/billableTimes/0/employee"), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void deleteProjectForbidden() throws Exception { assertThat(removeUrl("/billableTimes/0/project"), isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void updateEmployeeAllowedForSupervisor() throws Exception { assertThat(updateLink(0L, "employee", "/employees/0"), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void updateProjectAllowedForSupervisor() throws Exception { assertThat(updateLink(0L, "project", "/projects/0"), isNoContent()); } @Test public void updateEmployeeForbiddenForEmployee() throws Exception { assertThat(updateLink(0L, "employee", "/employees/0"), isForbidden()); } @Test public void updateProjectForbiddenForEmployee() throws Exception { assertThat(updateLink(0L, "project", "/projects/0"), isForbidden()); } @Test @OAuthRequest("ROLE_ADMIN") public void findByDateBetweenAllowedForAdmin() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/billableTimes/search/findByDateBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByDateBetweenForbiddenForSupervisor() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/billableTimes/search/findByDateBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/billtimes/BillableTimesJsonGenerator.java ================================================ package de.techdev.trackr.domain.project.billtimes; import de.techdev.test.rest.AbstractJsonGenerator; import de.techdev.trackr.util.LocalDateUtil; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.temporal.ChronoField; public class BillableTimesJsonGenerator extends AbstractJsonGenerator { private Long employeeId; private Long projectId; public BillableTimesJsonGenerator withEmployeeId(Long employeeId) { this.employeeId = employeeId; return this; } public BillableTimesJsonGenerator withProjectId(Long projectId) { this.projectId = projectId; return this; } @Override protected String getJsonRepresentation(BillableTime object) { StringWriter writer = new StringWriter(); if (employeeId == null || projectId == null) { throw new IllegalStateException("Employee id and project id must not be null."); } JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); jg.writeStartObject() .write("date", sdf.format(object.getDate())) .write("minutes", object.getMinutes()) .write("employee", "/employees/" + employeeId) .write("project", "/projects/" + projectId); if (object.getId() != null) { jg.write("id", object.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected BillableTime getNewTransientObject(int i) { BillableTime billableTime = new BillableTime(); LocalDate localDate = LocalDate.now().with(ChronoField.DAY_OF_YEAR, (i % 356) + 1); billableTime.setDate(LocalDateUtil.fromLocalDate(localDate)); billableTime.setMinutes(i); return billableTime; } @Override protected BillableTimesJsonGenerator getSelf() { return this; } @Override protected void reset() { employeeId = null; projectId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/invoice/InvoiceEventHandlerTest.java ================================================ package de.techdev.trackr.domain.project.invoice; import de.techdev.trackr.domain.company.Company; import de.techdev.trackr.util.LocalDateUtil; import org.junit.Before; import org.junit.Test; import java.time.LocalDate; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; public class InvoiceEventHandlerTest { private InvoiceEventHandler invoiceEventHandler; @Before public void setUp() throws Exception { invoiceEventHandler = new InvoiceEventHandler(); } @Test public void testSetInvoiceStateIfNecessaryOverdue() throws Exception { Invoice invoice = new Invoice(); LocalDate dueDate = LocalDate.of(2013, 10, 1); invoice.setDueDate(LocalDateUtil.fromLocalDate(dueDate)); invoiceEventHandler.setInvoiceStateIfNecessary(invoice); assertThat(invoice.getInvoiceState(), is(Invoice.InvoiceState.OVERDUE)); } @Test public void testSetInvoiceStateIfNecessaryOutstanding() throws Exception { Invoice invoice = new Invoice(); LocalDate dueDate = LocalDate.now().plusDays(7); invoice.setDueDate(LocalDateUtil.fromLocalDate(dueDate)); invoiceEventHandler.setInvoiceStateIfNecessary(invoice); assertThat(invoice.getInvoiceState(), is(Invoice.InvoiceState.OUTSTANDING)); } @Test public void testSetDueDateFromTimeForPayment() throws Exception { Invoice invoice = new Invoice(); Company company = new Company(); company.setTimeForPayment(14); invoice.setDebitor(company); invoice.setCreationDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 1))); invoiceEventHandler.setDueDateFromTimeForPayment(invoice); assertThat(invoice.getDueDate(), is(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 15)))); } @Test public void testDontSetDueDateFromTimeForPaymentIfItIsFilled() throws Exception { Invoice invoice = new Invoice(); Company company = new Company(); company.setTimeForPayment(14); invoice.setDebitor(company); invoice.setCreationDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 1))); invoice.setDueDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 2))); invoiceEventHandler.setDueDateFromTimeForPayment(invoice); assertThat(invoice.getDueDate(), is(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 2)))); } @Test public void testSetDueDateFromTimeForPaymentDontFailOnNoDebitor() throws Exception { Invoice invoice = new Invoice(); invoice.setCreationDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 1))); invoiceEventHandler.setDueDateFromTimeForPayment(invoice); } @Test public void testSetDueDateFromTimeForPaymentDontFailOnNoTimeForPayment() throws Exception { Invoice invoice = new Invoice(); Company company = new Company(); invoice.setDebitor(company); invoice.setCreationDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 1))); invoiceEventHandler.setDueDateFromTimeForPayment(invoice); } @Test public void stateGetsSetToOverdueIfTimeForPaymentSetsDueDateInPast() throws Exception { Invoice invoice = new Invoice(); Company company = new Company(); company.setTimeForPayment(14); invoice.setDebitor(company); invoice.setCreationDate(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 1))); invoiceEventHandler.authorizeCreate(invoice); assertThat(invoice.getDueDate(), is(LocalDateUtil.fromLocalDate(LocalDate.of(2014, 1, 15)))); assertThat(invoice.getInvoiceState(), is(Invoice.InvoiceState.OVERDUE)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/invoice/InvoiceJsonGenerator.java ================================================ package de.techdev.trackr.domain.project.invoice; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; public class InvoiceJsonGenerator extends AbstractJsonGenerator { private Long debitorId; public InvoiceJsonGenerator withDebitorId(Long debitorId) { this.debitorId = debitorId; return this; } @Override protected String getJsonRepresentation(Invoice object) { StringWriter writer = new StringWriter(); JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); jg.writeStartObject() .write("identifier", object.getIdentifier()) .write("invoiceState", object.getInvoiceState().toString()) .write("invoiceTotal", object.getInvoiceTotal()) .write("debitor", "/companies/" + debitorId) .write("creationDate", sdf.format(object.getCreationDate())); if (object.getDueDate() != null) { jg.write("dueDate", sdf.format(object.getDueDate())); } if (object.getId() != null) { jg.write("id", object.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected Invoice getNewTransientObject(int i) { Invoice invoice = new Invoice(); invoice.setIdentifier("identifier_" + i); invoice.setInvoiceState(Invoice.InvoiceState.OUTSTANDING); invoice.setDueDate(new Date()); invoice.setInvoiceTotal(BigDecimal.TEN); invoice.setCreationDate(new Date()); return invoice; } @Override protected InvoiceJsonGenerator getSelf() { return this; } @Override protected void reset() { debitorId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/invoice/InvoiceResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.project.invoice; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest("ROLE_ADMIN") public class InvoiceResourceSecurityTest extends AbstractDomainResourceSecurityTest { private InvoiceJsonGenerator jsonGenerator = new InvoiceJsonGenerator(); @Override protected String getResourceName() { return "invoices"; } @Test public void rootIsAccessibleForAdmin() throws Exception { assertThat(root(), isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void rootIsForbiddenForSupervisor() throws Exception { assertThat(root(), isForbidden()); } @Test public void oneIsAccessibleForAdmin() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void oneIsForbiddenForSupervisor() throws Exception { assertThat(one(0L), isForbidden()); } @Test public void findByInvoiceStateIsAccessibleForAdmin() throws Exception { assertThat(oneUrl("/invoices/search/findByInvoiceState?state=OUTSTANDING"), isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByInvoiceStateIsForbiddenForSupervisor() throws Exception { assertThat(oneUrl("/invoices/search/findByInvoiceState?state=OUTSTANDING"), isForbidden()); } @Test public void findByIdentifierLikeAndInvoiceStateIsAccessibleForAdmin() throws Exception { assertThat(oneUrl("/invoices/search/findByIdentifierLikeIgnoreCaseAndInvoiceState?identifier=TEST&state=OUTSTANDING"), isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByIdentifierLikeAndInvoiceStateIsForbiddenForSupervisor() throws Exception { assertThat(oneUrl("/invoices/search/findByIdentifierLikeIgnoreCaseAndInvoiceState?identifier=TEST&state=OUTSTANDING"), isForbidden()); } @Test public void findByCreationDateBetweenAccessibleForAdmin() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/invoices/search/findByCreationDateBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findByCreationDateBetweenForbiddenForSupervisor() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/invoices/search/findByCreationDateBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } @Test public void adminCanCreate() throws Exception { String json = jsonGenerator.start().withDebitorId(0L).build(); assertThat(create(json), isCreated()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void supervisorCannotCreate() throws Exception { String json = jsonGenerator.start().withDebitorId(0L).build(); assertThat(create(json), isForbidden()); } @Test public void adminCanDelete() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void supervisorCannotDelete() throws Exception { assertThat(remove(0L), isForbidden()); } @Test public void adminCanSetPaid() throws Exception { ResponseEntity response = restTemplate.postForEntity(host + "/invoices/0/markPaid", null, String.class); assertThat(response, isAccessible()); ResponseEntity invoice = restTemplate.getForEntity(host + "/invoices/0", Invoice.class); assertThat(invoice.getBody().getInvoiceState(), is(Invoice.InvoiceState.PAID)); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void supervisorCannotSetPaid() throws Exception { ResponseEntity response = restTemplate.postForEntity(host + "/invoices/0/markPaid", null, String.class); assertThat(response, isForbidden()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/CustomWorkTimeTest.java ================================================ package de.techdev.trackr.domain.project.worktimes; import org.junit.Test; import java.sql.Time; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class CustomWorkTimeTest { @Test public void reduceAndSortWorktimes() throws Exception { List reduced = CustomWorkTime.reduceAndSortWorktimes(createCustomWorkTimes()); assertThat(reduced.size(), is(2)); assertThat(reduced.get(0).getEnteredMinutes(), is(300L)); assertThat(reduced.get(1).getEnteredMinutes(), is(480L)); } @Test public void reduceAndSortWorkTimesPreservesComments() throws Exception { List reduced = CustomWorkTime.reduceAndSortWorktimes(createCustomWorkTimes()); assertThat(reduced.get(0).getComment(), is("Hallo1\nHallo2")); assertThat(reduced.get(1).getComment(), is("Hallo3")); } private List createCustomWorkTimes() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); CustomWorkTime ctw1 = new CustomWorkTime(); ctw1.setDate(sdf.parse("2014-01-01")); ctw1.setEnteredMinutes(240L); ctw1.setComment("Hallo1"); CustomWorkTime ctw2 = new CustomWorkTime(); ctw2.setDate(sdf.parse("2014-01-01")); ctw2.setComment("Hallo2"); ctw2.setEnteredMinutes(60L); CustomWorkTime ctw3 = new CustomWorkTime(); ctw3.setComment("Hallo3"); ctw3.setDate(sdf.parse("2014-01-02")); ctw3.setEnteredMinutes(480L); return asList(ctw1, ctw2, ctw3); } @Test public void customWorkTimeHourCalculation() throws Exception { WorkTime workTime21 = new WorkTime(); workTime21.setDate(new Date()); workTime21.setStartTime(Time.valueOf("09:00:00")); workTime21.setEndTime(Time.valueOf("17:00:00")); CustomWorkTime customWorkTime = CustomWorkTime.valueOf(workTime21); assertThat(customWorkTime.getEnteredMinutes(), is(480L)); } @Test public void customWorkTimeTransfersTheCommentFromTheWorkTime() throws Exception { WorkTime workTime21 = new WorkTime(); workTime21.setDate(new Date()); workTime21.setComment("Hallo"); workTime21.setStartTime(Time.valueOf("09:00:00")); workTime21.setEndTime(Time.valueOf("17:00:00")); CustomWorkTime customWorkTime = CustomWorkTime.valueOf(workTime21); assertThat(customWorkTime.getComment(), is("Hallo")); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeControllerSecurityTest.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static de.techdev.test.rest.DomainResourceTestMatchers.isForbidden; import static org.junit.Assert.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @OAuthRequest public class WorkTimeControllerSecurityTest extends AbstractRestIntegrationTest { @Test public void findEmployeeMappingByProjectAndDateBetweenForbiddenForEmployee() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/findEmployeeMappingByProjectAndDateBetween?project=0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } @Test @OAuthRequest("ROLE_SUPERVISOR") public void findEmployeeMappingByProjectAndDateBetweenAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/findEmployeeMappingByProjectAndDateBetween?project=0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeControllerTest.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.trackr.domain.employee.Employee; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.hateoas.EntityLinks; import org.springframework.hateoas.Link; import java.sql.Time; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.List; import java.util.Map; import static java.util.Arrays.asList; import static org.echocat.jomon.testing.BaseMatchers.isNotNull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * @author Moritz Schulze */ @RunWith(MockitoJUnitRunner.class) public class WorkTimeControllerTest { @InjectMocks private WorkTimeController workTimeController; @Mock private EntityLinks entityLinks; @Test public void convertStreamOfWorkTimesToMap() throws Exception { Link link = mock(Link.class); when(link.withSelfRel()).thenReturn(link); when(entityLinks.linkToSingleResource(Matchers.any(), Matchers.any())).thenReturn(link); Map map = workTimeController.convertStreamOfWorkTimesToMap(createTestWorktimes(), new HashMap<>()); assertThat("The map must contain two employee mappings", map.keySet().size(), is(2)); assertThat("One mapping must be for id 1", map.get(1L), isNotNull()); assertThat("One mapping must be for id 2", map.get(2L), isNotNull()); assertThat("The mapping for id 1 must have two work times", map.get(1L).getWorkTimes().size(), is(2)); assertThat("The mapping for id 2 must have one work time", map.get(2L).getWorkTimes().size(), is(1)); } private List createTestWorktimes() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Employee employee1 = new Employee(); employee1.setId(1L); employee1.setFirstName("employee_1"); employee1.setLastName("employee_1"); Employee employee2 = new Employee(); employee2.setId(2L); employee2.setFirstName("employee_2"); employee2.setLastName("employee_2"); WorkTime workTime11 = new WorkTime(); workTime11.setEmployee(employee1); workTime11.setDate(sdf.parse("2014-01-01")); workTime11.setStartTime(Time.valueOf("09:00:00")); workTime11.setEndTime(Time.valueOf("15:00:00")); WorkTime workTime12 = new WorkTime(); workTime12.setEmployee(employee1); workTime12.setDate(sdf.parse("2014-02-02")); workTime12.setStartTime(Time.valueOf("16:00:00")); workTime12.setEndTime(Time.valueOf("17:00:00")); WorkTime workTime21 = new WorkTime(); workTime21.setEmployee(employee2); workTime21.setDate(sdf.parse("2014-01-01")); workTime21.setStartTime(Time.valueOf("09:00:00")); workTime21.setEndTime(Time.valueOf("17:00:00")); return asList(workTime11, workTime12, workTime21); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeJsonGenerator.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.test.rest.AbstractJsonGenerator; import javax.json.stream.JsonGenerator; import java.io.StringWriter; import java.sql.Time; import java.text.SimpleDateFormat; import java.util.Date; public class WorkTimeJsonGenerator extends AbstractJsonGenerator { private Long employeeId; private Long projectId; public WorkTimeJsonGenerator withEmployeeId(Long employeeId) { this.employeeId = employeeId; return this; } public WorkTimeJsonGenerator withProjectId(Long projectId) { this.projectId = projectId; return this; } @Override protected String getJsonRepresentation(WorkTime object) { StringWriter writer = new StringWriter(); if (projectId == null || employeeId == null) { throw new IllegalStateException("employee id and project id must be set!"); } JsonGenerator jg = jsonGeneratorFactory.createGenerator(writer); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); jg.writeStartObject() .write("date", sdf.format(object.getDate())) .write("startTime", object.getStartTime().toString()) .write("endTime", object.getEndTime().toString()) .write("employee", "/employees/" + employeeId) .write("project", "/projects/" + projectId); if (object.getComment() != null) { jg.write("comment", object.getComment()); } if (object.getId() != null) { jg.write("id", object.getId()); } jg.writeEnd().close(); return writer.toString(); } @Override protected WorkTime getNewTransientObject(int i) { WorkTime workTime = new WorkTime(); workTime.setDate(new Date()); workTime.setStartTime(Time.valueOf("09:00:00")); workTime.setEndTime(Time.valueOf("17:00:00")); workTime.setComment("comment_" + i); return workTime; } @Override protected WorkTimeJsonGenerator getSelf() { return this; } @Override protected void reset() { projectId = null; employeeId = null; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeRepositoryTest.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.test.TransactionalIntegrationTest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import de.techdev.trackr.domain.employee.Employee; import de.techdev.trackr.domain.employee.EmployeeRepository; import de.techdev.trackr.domain.project.Project; import de.techdev.trackr.domain.project.ProjectRepository; import de.techdev.trackr.util.LocalDateUtil; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.jdbc.Sql; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.util.Date; import java.util.List; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @Sql("repositoryTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public class WorkTimeRepositoryTest extends TransactionalIntegrationTest { @Autowired private WorkTimeRepository workTimeRepository; @Autowired private EmployeeRepository employeeRepository; @Autowired private ProjectRepository projectRepository; /** * This finder must only respect the date but not time part of the second parameter. */ @Test public void findByEmployeeAndDateOnlyRespectsDatePart() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); Employee employee = employeeRepository.findOne(0L); List workTimes = workTimeRepository.findByEmployeeAndDateOrderByStartTimeAsc(employee, sdf.parse("2014-03-04 09:00:00")); assertThat(workTimes.size(), is(2)); } @Test public void findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc() throws Exception { Employee employee = employeeRepository.findOne(0L); Date low = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 3, 4)); Date high = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 3, 5)); List all = workTimeRepository.findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc(employee, low, high); assertThat(all.size(), is(3)); } @Test public void findByProjectAndDateBetweenOrderByDateAscStartTimeAsc() throws Exception { Project project = projectRepository.findOne(0L); Date low = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 3, 4)); Date high = LocalDateUtil.fromLocalDate(LocalDate.of(2014, 3, 5)); List all = workTimeRepository.findByProjectAndDateBetweenOrderByDateAscStartTimeAsc(project, low, high); assertThat(all.size(), is(3)); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeResourceSecurityTest.java ================================================ package de.techdev.trackr.domain.project.worktimes; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractDomainResourceSecurityTest; import org.junit.Ignore; import org.junit.Test; import org.springframework.http.ResponseEntity; import org.springframework.test.context.jdbc.Sql; import static de.techdev.test.rest.DomainResourceTestMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Sql("resourceTest.sql") @Sql(value = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @OAuthRequest public class WorkTimeResourceSecurityTest extends AbstractDomainResourceSecurityTest { private WorkTimeJsonGenerator jsonGenerator = new WorkTimeJsonGenerator(); @Override protected String getResourceName() { return "workTimes"; } @Test public void rootNotExported() throws Exception { assertThat(root(), isMethodNotAllowed()); } @Test public void oneAllowedForOwner() throws Exception { assertThat(one(0L), isAccessible()); } @Test @OAuthRequest(username = "someone.else@techdev.de") public void oneForbiddenForOther() throws Exception { assertThat(one(0L), isForbidden()); } @Test public void createAllowedForEveryoneIfIsEmployee() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).build(); assertThat(create(json), isCreated()); } @Test public void updateAllowedForOwner() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).apply(w -> w.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test @OAuthRequest(value = "ROLE_ADMIN", username = "admin@techdev.de") public void updateAllowedForAdmin() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).apply(w -> w.setId(0L)).build(); assertThat(update(0L, json), isUpdated()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void updateNotAllowedForSupervisor() throws Exception { String json = jsonGenerator.start().withEmployeeId(0L).withProjectId(0L).apply(w -> w.setId(0L)).build(); assertThat(update(0L, json), isForbidden()); } @Test public void deleteAllowedForOwner() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest(value = "ROLE_ADMIN", username = "admin@techdev.de") public void deleteAllowedForAdmin() throws Exception { assertThat(remove(0L), isNoContent()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void deleteNotAllowedForSupervisor() throws Exception { assertThat(remove(0L), isForbidden()); } /** * TODO: when trying to set to another employee the EmployeeRepository will deny accessing that employee which will result in a HTTP 400 * TODO: When trying to set to the same employee our Exception thrown in the {@link de.techdev.trackr.domain.project.worktimes.WorkTimeEventHandler} * TODO: won't be propagated to Web MVC. * TODO: so in conclusion updating does not work but will produce a 500 or 400 instead of a 405. * @throws Exception */ @Test @Ignore public void updateEmployeeNotAllowed() throws Exception { assertThat(updateLink(0L, "employee", "/employees/0"), isMethodNotAllowed()); } @Test @OAuthRequest(value = "ROLE_ADMIN") public void deleteEmployeeNotAllowed() throws Exception { assertThat(removeUrl("/workTimes/0/employee"), isForbidden()); } @Test @OAuthRequest(value = "ROLE_ADMIN") public void deleteProjectNotAllowed() throws Exception { assertThat(removeUrl("/workTimes/0/project"), isForbidden()); } @Test public void updateProjectAllowedForOwner() throws Exception { assertThat(updateLink(0L, "project", "/projects/0"), isNoContent()); } @Test @OAuthRequest(value = "ROLE_ADMIN", username = "admin@techdev.de") public void updateProjectAllowedForAdmin() throws Exception { assertThat(updateLink(0L, "project", "/projects/0"), isNoContent()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void updateProjectForbiddenForSupervisor() throws Exception { assertThat(updateLink(0L, "project", "/projects/0"), isForbidden()); } @Test public void findByEmployeeAndDateOrderByStartTimeAscAllowedForOwner() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByEmployeeAndDateOrderByStartTimeAsc?employee=/employees/0&date=2014-01-01", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void findByEmployeeAndDateOrderByStartTimeAscAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByEmployeeAndDateOrderByStartTimeAsc?employee=/employees/0&date=2014-01-01", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest(value = "ROLE_ADMIN") public void findByDateBetweenAllowedForAdmin() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByDateBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void findByDateBetweenForbiddenForSupervisor() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByDateBetween?start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } @Test public void findByEmployeeAndDateBetweenOrderByDateAscStartTimeAscAllowedForOwner() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc?employee=/employees/0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR", username = "supervisor@techdev.de") public void findByEmployeeAndDateBetweenOrderByDateAscStartTimeAscAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc?employee=/employees/0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } /** * This does not work because the accessDeniedException is thrown somewhere where spring-data-rest does not catch it. * So we get HTTP 400 instead of 403. * TODO: find out how to get a 403 */ @Test @Ignore @OAuthRequest(username = "someone.else@techdev.de") public void findByEmployeeAndDateBetweenOrderByDateAscStartTimeAscForbiddenForOther() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc?employee=/employees/0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } /** * This does not work because the accessDeniedException is thrown somewhere where spring-data-rest does not catch it. * So we get HTTP 400 instead of 403. * TODO: find out how to get a 403 */ @Test @Ignore @OAuthRequest(username = "someone.else@techdev.de") public void findByEmployeeAndDateOrderByStartTimeAscForbiddenForOther() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByEmployeeAndDateOrderByStartTimeAsc?employee=/employees/0&date=2014-01-01", String.class); assertThat(response, isForbidden()); } @Test @OAuthRequest(value = "ROLE_SUPERVISOR") public void findByProjectAndDateBetweenOrderByDateAscStartTimeAscAllowedForSupervisor() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByProjectAndDateBetweenOrderByDateAscStartTimeAsc?project=/projects/0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isAccessible()); } @Test public void findByProjectAndDateBetweenOrderByDateAscStartTimeAscForbiddenForEmployee() throws Exception { ResponseEntity response = restTemplate .getForEntity(host + "/workTimes/search/findByProjectAndDateBetweenOrderByDateAscStartTimeAsc?project=/projects/0&start=2014-01-01&end=2014-01-31", String.class); assertThat(response, isForbidden()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/scheduling/LastWorkdayDayOfMonthTriggerTest.java ================================================ package de.techdev.trackr.domain.scheduling; import de.techdev.trackr.domain.common.FederalState; import de.techdev.trackr.domain.employee.vacation.Holiday; import de.techdev.trackr.domain.employee.vacation.HolidayRepository; import de.techdev.trackr.domain.scheduling.LastWorkdayDayOfMonthTrigger; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.scheduling.TriggerContext; import java.time.DayOfWeek; import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.List; import static de.techdev.trackr.util.LocalDateUtil.fromDate; import static de.techdev.trackr.util.LocalDateUtil.fromLocalDate; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class LastWorkdayDayOfMonthTriggerTest { @InjectMocks private LastWorkdayDayOfMonthTrigger lastWorkdayDayOfMonthTrigger = new LastWorkdayDayOfMonthTrigger(); @Mock private HolidayRepository holidayRepository; private List holidays; @Before public void setUp() throws Exception { lastWorkdayDayOfMonthTrigger.setFederalState(FederalState.BERLIN); Holiday holiday = new Holiday(); holiday.setDay(fromLocalDate(LocalDate.of(2014, 5, 1))); List holidayList = new ArrayList<>(); holidayList.add(holiday); when(holidayRepository.findByFederalStateAndDayBetween(Matchers.any(), Matchers.any(), Matchers.any())).thenReturn(holidayList); holidays = new ArrayList<>(); holidays.add(fromDate(holiday.getDay())); } @Test public void lastDayIsNotWeekend() throws Exception { LocalDate march2014 = LocalDate.of(2014, 3, 1); LocalDate lastWeekdayInMonth = lastWorkdayDayOfMonthTrigger.lastWeekdayInMonth(march2014, holidays); assertThat(lastWeekdayInMonth.getDayOfWeek(), is(DayOfWeek.MONDAY)); } @Test public void lastDayIsSunday() throws Exception { LocalDate march2014 = LocalDate.of(2014, 8, 5); LocalDate lastWeekdayInMonth = lastWorkdayDayOfMonthTrigger.lastWeekdayInMonth(march2014, holidays); assertThat(lastWeekdayInMonth.getDayOfWeek(), is(DayOfWeek.FRIDAY)); } @Test public void lastDayIsSaturday() throws Exception { LocalDate march2014 = LocalDate.of(2014, 5, 2); LocalDate lastWeekdayInMonth = lastWorkdayDayOfMonthTrigger.lastWeekdayInMonth(march2014, holidays); assertThat(lastWeekdayInMonth.getDayOfWeek(), is(DayOfWeek.FRIDAY)); } @Test public void lastDayIsMondayAndHoliday() throws Exception { LocalDate june2014 = LocalDate.of(2014, 6, 1); List holidays = asList(LocalDate.of(2014, 6, 30)); LocalDate lastWeekDayInMonth = lastWorkdayDayOfMonthTrigger.lastWeekdayInMonth(june2014, holidays); assertThat(lastWeekDayInMonth.getDayOfWeek(), is(DayOfWeek.FRIDAY)); assertThat(lastWeekDayInMonth.getDayOfMonth(), is(27)); } @Test public void lastDayIsMondayAndHolidayAndTheFridayIsAHolidayToo() throws Exception { LocalDate june2014 = LocalDate.of(2014, 6, 1); List holidays = asList(LocalDate.of(2014, 6, 27), LocalDate.of(2014, 6, 30)); LocalDate lastWeekDayInMonth = lastWorkdayDayOfMonthTrigger.lastWeekdayInMonth(june2014, holidays); assertThat(lastWeekDayInMonth.getDayOfWeek(), is(DayOfWeek.THURSDAY)); assertThat(lastWeekDayInMonth.getDayOfMonth(), is(26)); } @Test public void triggerThisMonthIfLastScheduledTimeIsNull() throws Exception { LocalDate date = lastWorkdayDayOfMonthTrigger.nextExecutionTimeInternal( getTriggerContextWithLastScheduledExecutionTime(null)); assertThat(date.getMonth(), is(LocalDate.now().getMonth())); } @Test public void dontTriggerTwiceAMonth() throws Exception { LocalDate date = lastWorkdayDayOfMonthTrigger.nextExecutionTimeInternal( getTriggerContextWithLastScheduledExecutionTime(fromLocalDate(LocalDate.now()))); assertThat(date.getMonth(), is(LocalDate.now().plusMonths(1).getMonth())); } protected TriggerContext getTriggerContextWithLastScheduledExecutionTime(final Date time) { return new TriggerContext() { @Override public Date lastScheduledExecutionTime() { return time; } @Override public Date lastActualExecutionTime() { return null; } @Override public Date lastCompletionTime() { return null; } }; } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/translations/TranslationControllerSecurityTest.java ================================================ package de.techdev.trackr.domain.translations; import de.techdev.test.oauth.OAuthRequest; import de.techdev.test.rest.AbstractRestIntegrationTest; import org.junit.Test; import org.springframework.http.ResponseEntity; import static de.techdev.test.rest.DomainResourceTestMatchers.isAccessible; import static org.junit.Assert.assertThat; @OAuthRequest public class TranslationControllerSecurityTest extends AbstractRestIntegrationTest { @Test public void testGetTranslationsIsAccessible() throws Exception { ResponseEntity response = restTemplate.getForEntity(host + "/translations?locale=de", String.class); assertThat(response, isAccessible()); } } ================================================ FILE: src/test/java/de/techdev/trackr/domain/translations/TranslationControllerTest.java ================================================ package de.techdev.trackr.domain.translations; import de.techdev.trackr.domain.employee.SettingsRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.servlet.LocaleResolver; import java.util.Locale; import static org.echocat.jomon.testing.BaseMatchers.isNotEmpty; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class TranslationControllerTest { @InjectMocks private TranslationController translationController; @Mock private LocaleResolver localeResolver; @Mock private SettingsRepository settingsRepository; @Test public void testGetTranslations() throws Exception { MockHttpServletResponse response = new MockHttpServletResponse(); translationController.getTranslations(Locale.ENGLISH, response); assertThat(response.getStatus(), is(200)); assertThat(response.getContentAsString(), isNotEmpty()); } @Test public void testSetTranslations() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); translationController.setLocale(Locale.ENGLISH, request, response, () -> "admin"); verify(localeResolver, atLeastOnce()).setLocale(request, response, Locale.ENGLISH); } } ================================================ FILE: src/test/resources/de/techdev/trackr/domain/company/address/resourceTest.sql ================================================ -- data for the address resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/company/contactPerson/resourceTest.sql ================================================ -- data for the contact person resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, roles, company) VALUES (0, 0, 'Robert', 'Lake', 'r.lake@webshop.de', 'Mr', '0178/11234566', 'Sales Manager', 0); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/company/repositoryTest.sql ================================================ -- data for the company repository test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, roles, company) VALUES (0, 0, 'Robert', 'Lake', 'r.lake@webshop.de', 'Mr', '0178/11234566', 'Sales Manager', 0); --- with project INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (1, 0, 1001, 'webshop Ltd.', 0, 30); INSERT INTO project (id, dailyrate, fixedprice, hourlyrate, identifier, name, version, volume, company_id) VALUES (0, 30, 30, 30, '1001.1', 'Project', 0, 1000, 1); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/company/resourceTest.sql ================================================ -- data for the company resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO contactPerson (id, version, firstName, lastName, email, salutation, phone, roles, company) VALUES (0, 0, 'Robert', 'Lake', 'r.lake@webshop.de', 'Mr', '0178/11234566', 'Sales Manager', 0); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/expenses/report/comment/resourceTest.sql ================================================ -- data for the travel expense report comment resource test INSERT INTO employee (id, federalstate, firstname, hourlycostrate, joindate, lastname, phonenumber, salary, title, vacationentitlement, version, email, deleted) VALUES (0, 'BERLIN', 'Firstname', 100, '2014-06-06', 'Lastname', 'Phonenumber', 40000, 'Title', 30, 0, 'employee@techdev.de', false); INSERT INTO address (id, city, country, housenumber, street, version, zipcode) VALUES (0, 'City', 'Country', '13', 'Street', 0, 'zipcode'); INSERT INTO company (id, companyid, name, version, address_id, timeforpayment) VALUES (0, 1000, 'Company', 0, 0, 30); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (0, 0, 0, 'PENDING', '2015-01-25 09:30:21', 0); INSERT INTO travelexpensereportcomment (id, submissiondate, text, employee_id, travelexpensereport_id) VALUES (0, '2015-01-25 13:01:31', 'Comment 1', 0, 0); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/expenses/report/resourceTest.sql ================================================ -- data for travel expense report resource test INSERT INTO employee (id, federalstate, firstname, hourlycostrate, joindate, lastname, phonenumber, salary, title, vacationentitlement, version, email, deleted) VALUES (0, 'BERLIN', 'Firstname', 100, '2014-06-06', 'Lastname', 'Phonenumber', 40000, 'Title', 30, 0, 'employee@techdev.de', false); INSERT INTO employee (id, federalstate, firstname, hourlycostrate, joindate, lastname, phonenumber, salary, title, vacationentitlement, version, email, deleted) VALUES (1, 'BERLIN', 'Firstname', 100, '2014-06-06', 'Lastname', 'Phonenumber', 40000, 'Title', 30, 0, 'someone.else@techdev.de', false); INSERT INTO address (id, city, country, housenumber, street, version, zipcode) VALUES (0, 'City', 'Country', '13', 'Street', 0, 'zipcode'); INSERT INTO company (id, companyid, name, version, address_id, timeforpayment) VALUES (0, 1000, 'Company', 0, 0, 30); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (0, 0, 0, 'PENDING', '2015-01-25 09:30:21', 0); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (1, 0, 0, 'SUBMITTED', '2015-01-25 09:30:21', 0); INSERT INTO travelexpense (id, cost, fromdate, submissiondate, todate, type, vat, version, report_id, comment, paid) VALUES (0, 100, '2015-01-01', '2015-02-01 13:41:13', '2015-02-13', 'TAXI', 13, 0, 0, 'Comment', false); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/expenses/resourceTest.sql ================================================ -- data for the travel expense report comment resource test INSERT INTO employee (id, federalstate, firstname, hourlycostrate, joindate, lastname, phonenumber, salary, title, vacationentitlement, version, email, deleted) VALUES (0, 'BERLIN', 'Firstname', 100, '2014-06-06', 'Lastname', 'Phonenumber', 40000, 'Title', 30, 0, 'employee@techdev.de', false); INSERT INTO address (id, city, country, housenumber, street, version, zipcode) VALUES (0, 'City', 'Country', '13', 'Street', 0, 'zipcode'); INSERT INTO company (id, companyid, name, version, address_id, timeforpayment) VALUES (0, 1000, 'Company', 0, 0, 30); INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (0, 0, 0, 'PENDING', '2015-01-25 09:30:21', 0); INSERT INTO travelexpense (id, cost, fromdate, submissiondate, todate, type, vat, version, report_id, comment, paid) VALUES (0, 100, '2015-01-01', '2015-02-01 13:41:13', '2015-02-13', 'TAXI', 13, 0, 0, 'Comment', false); -- accepted report for various tests INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (1, 0, 0, 'APPROVED', '2015-01-25 09:30:21', 0); INSERT INTO travelexpense (id, cost, fromdate, submissiondate, todate, type, vat, version, report_id, comment, paid) VALUES (1, 100, '2015-01-01', '2015-02-01 13:41:13', '2015-02-13', 'TAXI', 13, 0, 1, 'Comment', false); -- submitted report for various tests INSERT INTO travelExpenseReport (id, version, employee_id, status, submissionDate, debitor_id) VALUES (2, 0, 0, 'SUBMITTED', '2015-01-25 09:30:21', 0); INSERT INTO travelexpense (id, cost, fromdate, submissiondate, todate, type, vat, version, report_id, comment, paid) VALUES (2, 100, '2015-01-01', '2015-02-01 13:41:13', '2015-02-13', 'TAXI', 13, 0, 2, 'Comment', true); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/login/resourceTest.sql ================================================ -- data for the principal controller resource test. INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO settings (id, type, value, employee_id) VALUES (0, 'LOCALE', 'en', 0) ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/resourceTest.sql ================================================ -- data for the employee resource test. INSERT INTO address (id, city, country, housenumber, street, version, zipcode) VALUES (0, 'Berlin', 'Deutschland', '11', 'Berliner Strasse', 0, '10111'); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, address_id, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, 0, false); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/sickdays/resourceTest.sql ================================================ -- data for the sick days resource test. INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO sickdays (id, enddate, startdate, version, employee_id) VALUES (0, '2014-01-05', '2014-01-01', 0, 0); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/vacation/holiday/resourceTest.sql ================================================ -- data for the holiday resource test. INSERT INTO holiday (id, day, federalstate, name) VALUES (0, '2014-12-24', 'BERLIN', 'Weihnachten'); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/vacation/repositoryTest.sql ================================================ -- data for the vacation request repository test. INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO vacationrequest (id, enddate, numberofdays, startdate, status, submissiontime, version, employee_id) VALUES (0, '2014-12-08', 5, '2014-10-01', 'APPROVED', '2014-10-15 19:21:56', 0, 0); INSERT INTO vacationrequest (id, enddate, numberofdays, startdate, status, submissiontime, version, employee_id) VALUES (1, '2014-12-08', 5, '2014-10-01', 'REJECTED', '2014-10-15 19:21:56', 0, 0); INSERT INTO vacationrequest (id, enddate, numberofdays, startdate, status, submissiontime, version, employee_id) VALUES (3, '2014-12-08', 5, '2014-10-01', 'PENDING', '2014-10-15 19:21:56', 0, 0); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/employee/vacation/resourceTest.sql ================================================ -- data for the vacation request resource test. INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO vacationrequest (id, enddate, numberofdays, startdate, status, submissiontime, version, employee_id) VALUES (0, '2015-02-06', 5, '2015-02-02', 'PENDING', '2015-01-20 19:21:56', 0, 0); -- approved and rejected requests for various tests INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (1, 0, 'employee2@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO vacationrequest (id, enddate, numberofdays, startdate, status, submissiontime, version, employee_id, approvaldate, approver_id) VALUES (1, '2015-02-06', 5, '2015-02-02', 'APPROVED', '2015-01-20 19:21:56', 0, 0, '2015-01-21 09:13:47', 1); INSERT INTO vacationrequest (id, enddate, numberofdays, startdate, status, submissiontime, version, employee_id, approvaldate, approver_id) VALUES (2, '2015-02-06', 5, '2015-02-02', 'REJECTED', '2015-01-20 19:21:56', 0, 0, '2015-01-21 09:13:47', 1); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/emptyDatabase.sql ================================================ DELETE FROM billabletime; DELETE FROM contactperson; DELETE FROM holiday; DELETE FROM invoice; DELETE FROM settings; DELETE FROM sickdays; DELETE FROM travelexpense; DELETE FROM travelexpensereportcomment; DELETE FROM travelexpensereport; DELETE FROM vacationrequest; DELETE FROM worktime; DELETE FROM project; DELETE FROM company; DELETE FROM employee; DELETE FROM address; ================================================ FILE: src/test/resources/de/techdev/trackr/domain/project/billtimes/resourceTest.sql ================================================ -- data for the billable time resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO project (id, version, identifier, name, company_id, volume, fixedPrice) VALUES (0, 0, '1000.2014.1', 'Webshop - Checkout Development', 0, 60, 15000); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO billabletime (id, date, minutes, version, employee, project) VALUES (0L, '2015-01-01', 680, 0, 0, 0); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/project/invoice/resourceTest.sql ================================================ -- data for the project resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO invoice (id, version, identifier, debitor, creationDate, invoiceTotal, invoiceState, dueDate) VALUES (0, 0, '1003.2014.1-2014.01-1', 0, '2014-01-04', 1500.00, 'PAID', '2014-02-01'); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/project/resourceTest.sql ================================================ -- data for the project resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (1, 0, 'Berliner Straße', '125', '60139', 'Frankfurt', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO company (id, version, companyId, name, address_id) VALUES (1, 0, 1001, 'finance Meier & partners', 1); INSERT INTO project (id, version, identifier, name, company_id, volume, fixedPrice, debitor_id) VALUES (0, 0, '1000.2014.1', 'Webshop - Checkout Development', 0, 60, 15000, 1); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (1, 0, 0, 0, '2014-06-02', '09:00:00', '17:15:00'); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/project/worktimes/repositoryTest.sql ================================================ -- data for the worktime repository test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO project (id, version, identifier, name, company_id, volume, fixedPrice) VALUES (0, 0, '1000.2014.1', 'Webshop - Checkout Development', 0, 60, 15000); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (0, 0, 0, 0, '2014-03-04', '11:00:00', '18:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (1, 0, 0, 0, '2014-03-04', '12:00:00', '15:00:00'); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (2, 0, 0, 0, '2014-03-05', '12:00:00', '15:00:00'); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/project/worktimes/resourceTest.sql ================================================ -- data for the worktime resource test. INSERT INTO address (id, version, street, houseNumber, zipCode, city, country) VALUES (0, 0, 'Sun Alley', '31', '15489', 'Munich', 'Deutschland'); INSERT INTO company (id, version, companyId, name, address_id, timeForPayment) VALUES (0, 0, 1000, 'webshop Ltd.', 0, 30); INSERT INTO project (id, version, identifier, name, company_id, volume, fixedPrice) VALUES (0, 0, '1000.2014.1', 'Webshop - Checkout Development', 0, 60, 15000); INSERT INTO employee (id, version, email, firstName, lastName, title, hourlyCostRate, salary, federalState, joinDate, vacationEntitlement, deleted) VALUES (0, 0, 'employee@techdev.de', 'John', 'Johnson', 'Software Engineer', 80, 10000, 'BERLIN', '2014-02-01', 30, false); INSERT INTO worktime (id, version, project, employee, date, startTime, endTime) VALUES (0, 0, 0, 0, '2014-06-02', '09:00:00', '17:15:00'); ================================================ FILE: src/test/resources/de/techdev/trackr/domain/tableUuidMapping.sql ================================================ CREATE TABLE IF NOT EXISTS uuid_mapping ( id int8 not null, uuid varchar(40) );