development f985c5f16ee6 cached
245 files
553.9 KB
151.7k tokens
1087 symbols
1 requests
Download .txt
Showing preview only (635K chars total). Download the full file or copy to clipboard to get everything.
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<String, String> 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<MailMessage> 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<String> 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<String> 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<String> 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<HandlerExceptionResolver> 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<Class<?>> 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 <i>should</i> 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<BackendIdConverter, Class<?>> 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<BackendIdConverter, Class<?>> 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<String, Date> {

    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<FederalState> 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.
 * <p>
 * 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<Address, Long> {

    @Override
    @RestResource(exported = false)
    List<Address> findAll();

    @Override
    @RestResource(exported = false)
    Page<Address> findAll(Pageable pageable);

    @Override
    @RestResource(exported = false)
    List<Address> 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<ContactPerson> contactPersons = new ArrayList<>();

    @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "company")
    private List<Project> projects = new ArrayList<>();

    @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "debitor")
    private List<Project> 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<ContactPerson> 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, Long> {

    Company findByCompanyId(@Param("companyId") Long companyId);

    List<Company> 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<ContactPerson> 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<ContactPerson, Long> {

}


================================================
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<WorkTime> workTimes = new ArrayList<>();

    @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "employee")
    private List<BillableTime> billableTimes = new ArrayList<>();

    @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "employee")
    private List<VacationRequest> vacationRequests;

    @OneToMany(mappedBy = "approver")
    private List<VacationRequest> approvedRequests;

    @OneToMany(mappedBy = "employee")
    private List<Report> 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<Employee, Long> {

    @Override
    @PostAuthorize("hasRole('ROLE_SUPERVISOR') or returnObject?.email == principal?.username")
    Employee findOne(@Param("id") Long id);

    @Override
    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    List<Employee> findAll();

    @Override
    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    List<Employee> findAll(Sort sort);

    @Override
    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    List<Employee> findAll(Iterable<Long> longs);

    @Override
    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    Page<Employee> findAll(Pageable pageable);

    @RestResource(exported = false)
    List<Employee> 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<Employee> 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, Long> {

    Settings save(Settings settings);

    List<Settings> 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<EmployeeForAddressBookDTO> getAddressList(
            @PageableDefault(sort = "lastName") Pageable pageable
    ) {
        Page<Employee> pageOfEmployees = employeeRepository.findAllForAddressBook(pageable);
        List<EmployeeForAddressBookDTO> 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<EmployeeForAddressBookDTO> transformToReducedEmployees(List<Employee> 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<TravelExpense, Long> {

    @Override
    @RestResource(exported = false)
    Iterable<TravelExpense> 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<TravelExpense.Type> 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<TravelExpense> 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<TravelExpense> getExpenses();

        Report.Status getStatus();

        Date getSubmissionDate();
    }

    @Projection(types = Report.class, name = "withExpensesAndDebitor")
    public interface TravelExpenseReportWithExpensesAndDebitorProjection {
        Long getId();

        Integer getVersion();

        List<TravelExpense> 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<TravelExpense> 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<Comment> 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<byte[]> 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<TravelExpense> 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<Date> startDate = expenses.stream().map(TravelExpense::getFromDate).min(Date::compareTo);
        Optional<Date> 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<TravelExpense> 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<Report, Long> {

    @Override
    @RestResource(exported = false)
    Iterable<Report> findAll();

    @Override
    @PostAuthorize("hasRole('ROLE_SUPERVISOR') or returnObject?.employee?.email == principal?.username")
    Report findOne(Long aLong);

    @PreAuthorize("#employee.email == principal?.username")
    Page<Report> findByEmployeeAndStatusOrderByStatusAsc(@Param("employee") Employee employee, @Param("status") Report.Status status, Pageable pageable);

    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    Page<Report> findByStatus(@Param("status") Report.Status status, Pageable pageable);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    List<Report> 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<Comment, Long> {

    @Override
    @RestResource(exported = false)
    Iterable<Comment> 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<Comment> 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<? extends GrantedAuthority> 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<String> withoutThese) {
        Collection<String> 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<SickDays, Long> {

    @Override
    @RestResource(exported = false)
    Page<SickDays> findAll(Pageable pageable);

    @Override
    @PostAuthorize("hasRole('ROLE_ADMIN') or principal?.username == returnObject.employee.email")
    SickDays findOne(Long aLong);

    @PreAuthorize("#employee.email == principal?.username")
    List<SickDays> findByEmployee(@Param("employee") Employee employee);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    List<SickDays> 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<Holiday> 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<LocalDate> 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<LocalDate> 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<Holiday, Long> {

    @Override
    @RestResource(exported = false)
    <S extends Holiday> S save(S entity);

    @Override
    @RestResource(exported = false)
    void delete(Long id);

    List<Holiday> 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.
     * <p>
     * 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<VacationRequest> 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<String, Integer> 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<VacationRequest, Long> {

    @Override
    @PostAuthorize("hasRole('ROLE_SUPERVISOR') or principal?.username == returnObject.employee.email")
    VacationRequest findOne(Long aLong);

    @Override
    @RestResource(exported = false)
    Iterable<VacationRequest> findAll();

    @PreAuthorize("hasRole('ROLE_SUPERVISOR') or principal?.username == #employee.email")
    List<VacationRequest> findByEmployeeOrderByStartDateAsc(@Param("employee") Employee employee);

    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    List<VacationRequest> findByStatusOrderBySubmissionTimeAsc(@Param("status") VacationRequest.VacationRequestStatus status);

    @RestResource(exported = false)
    List<VacationRequest> 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<VacationRequest> 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<String, Integer> mapVacationRequestsToTotalDays(Date start, Date end) {
        List<VacationRequest> 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<String, Integer> mapToEmployeesAndSumUp(List<VacationRequest> vacationRequests, ToIntFunction<VacationRequest> 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<Employee> 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<WorkTime> workTimes = new ArrayList<>();

    @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "project")
    private List<BillableTime> 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, Long> {

    Project findByIdentifier(@Param("identifier") String identifier);

    List<Project> 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<String, Integer> findEmployeeMappingByProjectAndDateBetween(@RequestParam("project") String projectId,
                                                           @RequestParam("start") Date start,
                                                           @RequestParam("end") Date end) {
        Project project = conversionService.convert(Long.valueOf(projectId), Project.class);
        List<BillableTime> 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<BillableTime, Long> {
    @Override
    @RestResource(exported = false)
    Iterable<BillableTime> findAll();

    @Override
    @RestResource(exported = false)
    Iterable<BillableTime> findAll(Iterable<Long> longs);

    @Override
    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    BillableTime findOne(Long aLong);

    @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    List<BillableTime> findByProjectAndDateBetweenOrderByDateAsc(@Param("project") Project project,
                                                                 @Param("start") Date start,
                                                                 @Param("end") Date end);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    List<BillableTime> 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<Invoice> 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;
imp
Download .txt
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
Download .txt
SYMBOL INDEX (1087 symbols across 191 files)

FILE: src/main/java/de/techdev/trackr/Trackr.java
  class Trackr (line 18) | @SpringBootApplication(exclude = MailSenderAutoConfiguration.class)
    method primaryDataSource (line 22) | @Bean
    method handlePid (line 29) | @PostConstruct
    method main (line 36) | public static void main(String[] args) {

FILE: src/main/java/de/techdev/trackr/core/mail/GMailConfiguration.java
  class GMailConfiguration (line 21) | @Configuration
    method mailSender (line 29) | @Bean
    method applyProperties (line 36) | private void applyProperties(JavaMailSenderImpl sender) {
    method asProperties (line 52) | private Properties asProperties(Map<String, String> source) {
    method mailSession (line 61) | @Bean

FILE: src/main/java/de/techdev/trackr/core/mail/MailService.java
  type MailService (line 8) | public interface MailService {
    method sendMail (line 10) | void sendMail(SimpleMailMessage mailMessage);

FILE: src/main/java/de/techdev/trackr/core/mail/support/AsyncMailService.java
  class AsyncMailService (line 16) | @Component
    method sendMail (line 23) | @Override

FILE: src/main/java/de/techdev/trackr/core/mail/support/NoOpJavaMailSender.java
  class NoOpJavaMailSender (line 14) | @Slf4j
    method createMimeMessage (line 19) | @Override
    method createMimeMessage (line 25) | @Override
    method send (line 31) | @Override
    method send (line 36) | @Override
    method send (line 41) | @Override
    method send (line 46) | @Override
    method send (line 51) | @Override
    method send (line 64) | @Override

FILE: src/main/java/de/techdev/trackr/core/pdf/HtmlPdfConverter.java
  class HtmlPdfConverter (line 12) | public class HtmlPdfConverter {
    method HtmlPdfConverter (line 16) | public HtmlPdfConverter() {
    method renderHtmlToPdf (line 25) | public byte[] renderHtmlToPdf(String htmlContent) throws PdfCreationEx...

FILE: src/main/java/de/techdev/trackr/core/pdf/PdfCreationException.java
  class PdfCreationException (line 6) | public class PdfCreationException extends Exception {
    method PdfCreationException (line 7) | public PdfCreationException(Exception e) {

FILE: src/main/java/de/techdev/trackr/core/pdf/PdfRenderer.java
  class PdfRenderer (line 12) | @Slf4j
    method PdfRenderer (line 26) | public PdfRenderer() {
    method setUpRenderer (line 30) | @PostConstruct
    method renderPdf (line 35) | public byte[] renderPdf(String templateName, Context context) throws P...

FILE: src/main/java/de/techdev/trackr/core/pdf/ThymeleafRenderer.java
  class ThymeleafRenderer (line 13) | @Slf4j
    method ThymeleafRenderer (line 22) | public ThymeleafRenderer(String templatePath, String templateSuffix) {
    method setUpTemplateEngine (line 28) | private void setUpTemplateEngine() {
    method renderTemplateToHtml (line 47) | public String renderTemplateToHtml(String templateName, Context contex...

FILE: src/main/java/de/techdev/trackr/core/security/AuthorityService.java
  type AuthorityService (line 8) | public interface AuthorityService {
    method getEmployeeEmailsByAuthority (line 13) | Collection<String> getEmployeeEmailsByAuthority(String authority);

FILE: src/main/java/de/techdev/trackr/core/security/InMemoryAuthorityService.java
  class InMemoryAuthorityService (line 13) | @Service
    method getEmployeeEmailsByAuthority (line 20) | @Override

FILE: src/main/java/de/techdev/trackr/core/security/InMemorySecurityConfiguration.java
  class InMemorySecurityConfiguration (line 24) | @EnableWebSecurity
    method configure (line 32) | @Override
    method configure (line 45) | @Override

FILE: src/main/java/de/techdev/trackr/core/security/MethodSecurityConfiguration.java
  class MethodSecurityConfiguration (line 21) | @Profile("granular-security")
    method createExpressionHandler (line 32) | @Override
    method roleHierarchy (line 42) | @Bean
    method roleVoter (line 49) | @Bean

FILE: src/main/java/de/techdev/trackr/core/security/OAuth2AuthorityService.java
  class OAuth2AuthorityService (line 12) | @Service
    method OAuth2AuthorityService (line 18) | @Autowired
    method getEmployeeEmailsByAuthority (line 23) | @Override

FILE: src/main/java/de/techdev/trackr/core/security/OAuth2ResourceServerConfiguration.java
  class OAuth2ResourceServerConfiguration (line 24) | @Profile("oauth")
    method oauthDataSource (line 32) | @Bean
    method tokenStore (line 39) | @Bean
    method configure (line 45) | @Override
    method configure (line 50) | @Override

FILE: src/main/java/de/techdev/trackr/core/web/api/ApiRepositoryRestConfiguration.java
  class ApiRepositoryRestConfiguration (line 27) | @Configuration
    method configureRepositoryRestConfiguration (line 30) | @Override
    method configureConversionService (line 39) | @Override
    method dateConverter (line 45) | @Bean
    method configureValidatingRepositoryEventListener (line 53) | @Override
    method validator (line 62) | @Bean
    method messageSource (line 72) | @Bean

FILE: src/main/java/de/techdev/trackr/core/web/api/ApiWebMvcConfiguration.java
  class ApiWebMvcConfiguration (line 16) | @Configuration
    method entityLinks (line 26) | @Override
    method localeResolver (line 33) | @Bean
    method addFormatters (line 38) | @Override
    method jsonMappingHandlerExceptionResolver (line 51) | @Bean
    method extendHandlerExceptionResolvers (line 56) | @Override

FILE: src/main/java/de/techdev/trackr/core/web/api/ArgumentResolverPagingAndSortingTemplateVariables.java
  class ArgumentResolverPagingAndSortingTemplateVariables (line 24) | @Deprecated
    method ArgumentResolverPagingAndSortingTemplateVariables (line 30) | public ArgumentResolverPagingAndSortingTemplateVariables(HateoasPageab...
    method getPaginationTemplateVariables (line 37) | public TemplateVariables getPaginationTemplateVariables(MethodParamete...
    method getSortTemplateVariables (line 41) | public TemplateVariables getSortTemplateVariables(MethodParameter para...
    method enhance (line 45) | public void enhance(UriComponentsBuilder builder, MethodParameter para...
    method supportsParameter (line 54) | public boolean supportsParameter(MethodParameter parameter) {

FILE: src/main/java/de/techdev/trackr/core/web/api/ExceptionHandlers.java
  class ExceptionHandlers (line 21) | @ControllerAdvice
    method ExceptionHandlers (line 27) | @Autowired
    method handleJpaSystemException (line 38) | @ExceptionHandler(JpaSystemException.class)
    method handleRepositoryConstraintViolationException (line 50) | @ResponseBody

FILE: src/main/java/de/techdev/trackr/core/web/api/JsonMappingHandlerExceptionResolver.java
  class JsonMappingHandlerExceptionResolver (line 27) | public class JsonMappingHandlerExceptionResolver implements HandlerExcep...
    method JsonMappingHandlerExceptionResolver (line 31) | public JsonMappingHandlerExceptionResolver() {
    method resolveException (line 35) | @Override
    method writeExceptionAsJsonToOutput (line 66) | protected void writeExceptionAsJsonToOutput(InvalidFormatException ex,...
    method getFieldPath (line 84) | private String getFieldPath(InvalidFormatException ex) {

FILE: src/main/java/de/techdev/trackr/core/web/api/RepositoryEntityLinksWithoutProjection.java
  class RepositoryEntityLinksWithoutProjection (line 22) | @Deprecated
    method RepositoryEntityLinksWithoutProjection (line 37) | public RepositoryEntityLinksWithoutProjection(Repositories repositorie...
    method linkToSingleResource (line 43) | @Override

FILE: src/main/java/de/techdev/trackr/core/web/converters/DateConverter.java
  class DateConverter (line 15) | public class DateConverter implements Converter<String, Date> {
    method DateConverter (line 21) | public DateConverter() {
    method convert (line 26) | @Override

FILE: src/main/java/de/techdev/trackr/domain/ApiBeansConfiguration.java
  class ApiBeansConfiguration (line 25) | @Configuration
    method travelExpenseReportService (line 31) | @Bean
    method travelExpenseReportNotifyService (line 36) | @Bean
    method workTimeTrackingReminderService (line 41) | @Bean
    method vacationRequestNotifyService (line 46) | @Bean
    method vacationRequestService (line 51) | @Bean
    method vacationRequestEmployeeToDaysTotalService (line 56) | @Bean
    method holidayCalculator (line 61) | @Bean
    method invoiceOverdueService (line 66) | @Bean
    method changeStateService (line 71) | @Bean
    method sickDaysNotifyService (line 76) | @Bean
    method supervisorService (line 81) | @Bean
    method pdfRenderer (line 86) | @Bean
    method uuidMapper (line 91) | @Bean

FILE: src/main/java/de/techdev/trackr/domain/common/EmployeeSettingsLocaleResolver.java
  class EmployeeSettingsLocaleResolver (line 19) | public class EmployeeSettingsLocaleResolver implements LocaleContextReso...
    method resolveLocale (line 24) | @Override
    method setLocale (line 29) | @Override
    method resolveLocaleContext (line 36) | @Override
    method setLocaleContext (line 44) | @Override
    method getLocaleFromSessionOrAuthentication (line 57) | protected Locale getLocaleFromSessionOrAuthentication(HttpServletReque...

FILE: src/main/java/de/techdev/trackr/domain/common/FederalState.java
  type FederalState (line 8) | @JsonFormat(shape= JsonFormat.Shape.OBJECT)
    method FederalState (line 17) | FederalState(String state) {
    method getState (line 21) | public String getState() {
    method getName (line 25) | public String getName() {

FILE: src/main/java/de/techdev/trackr/domain/common/FederalStateController.java
  class FederalStateController (line 16) | @Controller
    method federalStates (line 20) | @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLI...

FILE: src/main/java/de/techdev/trackr/domain/common/UuidMapper.java
  class UuidMapper (line 17) | public class UuidMapper {
    method extractUUIDFromString (line 24) | public String extractUUIDFromString(String input) {
    method getIdFromUUID (line 33) | public Long getIdFromUUID(String uuid) {
    method deleteUUID (line 48) | public void deleteUUID(String uuid) {
    method deleteUUID (line 58) | public void deleteUUID(Long id) {
    method createUUID (line 68) | public UUID createUUID(Long id) {

FILE: src/main/java/de/techdev/trackr/domain/company/Address.java
  class Address (line 9) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/company/AddressEventHandler.java
  class AddressEventHandler (line 17) | @RepositoryEventHandler(Address.class)
    method checkSaveAuthority (line 21) | @HandleBeforeCreate
    method checkUpdateAuthority (line 26) | @HandleBeforeSave
    method checkDeleteAuthority (line 31) | @HandleBeforeDelete

FILE: src/main/java/de/techdev/trackr/domain/company/AddressRepository.java
  type AddressRepository (line 14) | public interface AddressRepository extends JpaRepository<Address, Long> {
    method findAll (line 16) | @Override
    method findAll (line 20) | @Override
    method findAll (line 24) | @Override
    method delete (line 28) | @Override
    method delete (line 32) | @Override

FILE: src/main/java/de/techdev/trackr/domain/company/Company.java
  class Company (line 19) | @Getter

FILE: src/main/java/de/techdev/trackr/domain/company/CompanyEventHandler.java
  class CompanyEventHandler (line 11) | @RepositoryEventHandler(Company.class)
    method checkSaveAuthority (line 15) | @HandleBeforeCreate
    method checkUpdateAuthority (line 20) | @HandleBeforeSave
    method checkDeleteAuthority (line 25) | @HandleBeforeDelete
    method beforeAddContactPerson (line 30) | @HandleBeforeLinkSave
    method beforeDeleteContactPerson (line 35) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/company/CompanyRepository.java
  type CompanyRepository (line 12) | @RepositoryRestResource(path = "/companies")
    method findByCompanyId (line 15) | Company findByCompanyId(@Param("companyId") Long companyId);
    method findByNameLikeIgnoreCaseOrderByNameAsc (line 17) | List<Company> findByNameLikeIgnoreCaseOrderByNameAsc(@Param("name") St...

FILE: src/main/java/de/techdev/trackr/domain/company/CompanyWithAddressAndContactPersonsProjection.java
  type CompanyWithAddressAndContactPersonsProjection (line 10) | @Projection(types = Company.class, name = "withAddressAndContactPersons")
    method getId (line 13) | Long getId();
    method getVersion (line 15) | Integer getVersion();
    method getCompanyId (line 17) | Long getCompanyId();
    method getName (line 19) | String getName();
    method getTimeForPayment (line 21) | Integer getTimeForPayment();
    method getAddress (line 23) | Address getAddress();
    method getContactPersons (line 25) | List<ContactPerson> getContactPersons();

FILE: src/main/java/de/techdev/trackr/domain/company/ContactPerson.java
  class ContactPerson (line 14) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/company/ContactPersonEventHandler.java
  class ContactPersonEventHandler (line 9) | @RepositoryEventHandler(ContactPerson.class)
    method checkCreateAuthority (line 13) | @HandleBeforeCreate
    method checkUpdateAuthority (line 18) | @HandleBeforeSave
    method checkDeleteAuthority (line 23) | @HandleBeforeDelete
    method checkLinkUpdateAuthority (line 28) | @HandleBeforeLinkSave
    method checkLinkDeleteAuthority (line 34) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/company/ContactPersonRepository.java
  type ContactPersonRepository (line 8) | public interface ContactPersonRepository extends CrudRepository<ContactP...

FILE: src/main/java/de/techdev/trackr/domain/employee/Employee.java
  class Employee (line 26) | @Entity
    method fullName (line 91) | public String fullName() {

FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeController.java
  class EmployeeController (line 15) | @Controller
    method updateSelf (line 30) | @ResponseBody
    method get (line 41) | @RequestMapping(value = "/{employee}/self", method = {RequestMethod.GET})

FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeEventHandler.java
  class EmployeeEventHandler (line 7) | @RepositoryEventHandler(Employee.class)
    method checkCreateAuthority (line 14) | @HandleBeforeCreate
    method createInitialSettings (line 19) | @HandleAfterCreate
    method checkUpdateAuthority (line 28) | @HandleBeforeSave
    method checkDeleteAuthority (line 33) | @HandleBeforeDelete
    method deleteCredentialForbidden (line 38) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeRepository.java
  type EmployeeRepository (line 16) | public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    method findOne (line 18) | @Override
    method findAll (line 22) | @Override
    method findAll (line 26) | @Override
    method findAll (line 30) | @Override
    method findAll (line 34) | @Override
    method findByFederalState (line 38) | @RestResource(exported = false)
    method findAllForAddressBook (line 46) | @RestResource(exported = false)
    method findByEmail (line 50) | @RestResource(exported = false)

FILE: src/main/java/de/techdev/trackr/domain/employee/EmployeeScheduledJob.java
  class EmployeeScheduledJob (line 9) | @Slf4j
    method sendWorkTimeReminderTask (line 22) | public Runnable sendWorkTimeReminderTask(FederalState state) {

FILE: src/main/java/de/techdev/trackr/domain/employee/Projections.java
  class Projections (line 10) | public class Projections {
    type WithAddressProjection (line 12) | @Projection(types = Employee.class, name = "withAddress")
      method getId (line 15) | Long getId();
      method getVersion (line 17) | Integer getVersion();
      method getFirstName (line 19) | String getFirstName();
      method getLastName (line 21) | String getLastName();
      method getEmail (line 23) | String getEmail();
      method getPhoneNumber (line 25) | String getPhoneNumber();
      method getTitle (line 27) | String getTitle();
      method getSalary (line 29) | BigDecimal getSalary();
      method getHourlyCostRate (line 31) | BigDecimal getHourlyCostRate();
      method getJoinDate (line 33) | Date getJoinDate();
      method getLeaveDate (line 35) | Date getLeaveDate();
      method getFederalState (line 37) | FederalState getFederalState();
      method getVacationEntitlement (line 39) | Float getVacationEntitlement();
      method getAddress (line 41) | Address getAddress();
      method isDeleted (line 43) | boolean isDeleted();

FILE: src/main/java/de/techdev/trackr/domain/employee/SelfEmployee.java
  class SelfEmployee (line 11) | @Getter
    method valueOf (line 34) | public static SelfEmployee valueOf(Employee employee) {

FILE: src/main/java/de/techdev/trackr/domain/employee/SelfEmployeeRepository.java
  class SelfEmployeeRepository (line 10) | @Repository
    method save (line 26) | @Transactional
    method findOne (line 42) | public SelfEmployee findOne(Long employeeId) {

FILE: src/main/java/de/techdev/trackr/domain/employee/Settings.java
  class Settings (line 10) | @Entity
    type SettingsType (line 16) | public enum SettingsType {

FILE: src/main/java/de/techdev/trackr/domain/employee/SettingsRepository.java
  type SettingsRepository (line 8) | @RepositoryRestResource(exported = false)
    method save (line 11) | Settings save(Settings settings);
    method findByEmployee_Email (line 13) | List<Settings> findByEmployee_Email(String email);
    method findByTypeAndEmployee_Email (line 15) | Settings findByTypeAndEmployee_Email(Settings.SettingsType type, Strin...

FILE: src/main/java/de/techdev/trackr/domain/employee/addressbook/AddressBookController.java
  class AddressBookController (line 23) | @Controller
    method getAddressList (line 30) | @PreAuthorize("hasRole('ROLE_EMPLOYEE')")
    method transformToReducedEmployees (line 47) | protected List<EmployeeForAddressBookDTO> transformToReducedEmployees(...
    method pageMetadataFromPage (line 54) | protected PagedResources.PageMetadata pageMetadataFromPage(Page page) {

FILE: src/main/java/de/techdev/trackr/domain/employee/addressbook/EmployeeForAddressBookDTO.java
  class EmployeeForAddressBookDTO (line 11) | @Getter
    method valueOf (line 29) | static EmployeeForAddressBookDTO valueOf(Employee employee) {

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpense.java
  class TravelExpense (line 13) | @Getter
    type Type (line 19) | public enum Type {

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseEventHandler.java
  class TravelExpenseEventHandler (line 7) | @RepositoryEventHandler(TravelExpense.class)
    method checkCreateAuthority (line 11) | @HandleBeforeCreate
    method checkUpdateAuthority (line 16) | @HandleBeforeSave
    method checkDeleteAuthority (line 21) | @HandleBeforeDelete
    method denyLinkSave (line 26) | @HandleBeforeLinkSave
    method denyLinkDelete (line 32) | @HandleBeforeLinkDelete
    method canCreate (line 38) | public boolean canCreate(String email, TravelExpense travelExpense) {
    method canEdit (line 44) | public boolean canEdit(String email, TravelExpense travelExpense) {
    method canDelete (line 50) | public boolean canDelete(String email, TravelExpense travelExpense) {

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseRepository.java
  type TravelExpenseRepository (line 10) | public interface TravelExpenseRepository extends CrudRepository<TravelEx...
    method findAll (line 12) | @Override
    method findOne (line 16) | @Override

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseTypeController.java
  class TravelExpenseTypeController (line 16) | @Controller
    method types (line 20) | @ResponseBody

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/Projections.java
  class Projections (line 15) | public class Projections {
    type TravelExpenseReportForOverviewProjection (line 16) | @Projection(types = Report.class, name = "overview")
      method getId (line 18) | Long getId();
      method getEmployee (line 20) | Employee getEmployee();
      method getStatus (line 22) | Report.Status getStatus();
      method getExpenses (line 24) | List<TravelExpense> getExpenses();
      method getSubmissionDate (line 26) | Date getSubmissionDate();
      method getApprovalDate (line 28) | Date getApprovalDate();
      method getApprover (line 30) | Employee getApprover();
      method getDebitor (line 32) | Company getDebitor();
      method getProject (line 34) | Project getProject();
    type TravelExpenseReportWithEmployeeAndTravelExpensesProjection (line 37) | @Projection(types = Report.class, name = "withEmployeeAndExpenses")
      method getId (line 39) | Long getId();
      method getVersion (line 41) | Integer getVersion();
      method getEmployee (line 43) | Employee getEmployee();
      method getExpenses (line 45) | List<TravelExpense> getExpenses();
      method getStatus (line 47) | Report.Status getStatus();
      method getSubmissionDate (line 49) | Date getSubmissionDate();
    type TravelExpenseReportWithExpensesAndDebitorProjection (line 52) | @Projection(types = Report.class, name = "withExpensesAndDebitor")
      method getId (line 54) | Long getId();
      method getVersion (line 56) | Integer getVersion();
      method getExpenses (line 58) | List<TravelExpense> getExpenses();
      method getStatus (line 60) | Report.Status getStatus();
      method getSubmissionDate (line 62) | Date getSubmissionDate();
      method getApprovalDate (line 64) | Date getApprovalDate();
      method getDebitor (line 66) | Company getDebitor();

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/Report.java
  class Report (line 20) | @Getter
    type Status (line 27) | public enum Status {

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportController.java
  class ReportController (line 22) | @Controller
    method submit (line 35) | @RequestMapping(value = "/{id}/submit", method = RequestMethod.PUT)
    method approve (line 42) | @RequestMapping(value = "/{id}/approve", method = RequestMethod.PUT)
    method reject (line 49) | @RequestMapping(value = "/{id}/reject", method = RequestMethod.PUT)
    method asPdf (line 56) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #travelExpenseReport.empl...

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportEventHandler.java
  class ReportEventHandler (line 8) | @RepositoryEventHandler(Report.class)
    method checkCreateAuthority (line 12) | @HandleBeforeCreate
    method checkUpdateAuthority (line 17) | @HandleBeforeSave
    method checkDeleteAuthority (line 22) | @HandleBeforeDelete
    method checkLinkSaveAuthority (line 27) | @HandleBeforeLinkSave
    method checkLinkDeleteAuthority (line 38) | @HandleBeforeLinkDelete
    method employeeCanDeleteReport (line 46) | public boolean employeeCanDeleteReport(Report report, String username) {

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportNotifyService.java
  class ReportNotifyService (line 15) | public class ReportNotifyService {
    method notifySupervisorsOnSubmission (line 34) | public void notifySupervisorsOnSubmission(Report report) {
    method getWebLink (line 50) | protected String getWebLink(Report report) {
    method notifyEmployeeOnApproval (line 54) | public void notifyEmployeeOnApproval(Report report) {
    method notifyEmployeeOnRejection (line 58) | public void notifyEmployeeOnRejection(Report report) {
    method notifyEmployeeOnStatusChange (line 62) | private void notifyEmployeeOnStatusChange(Report report, String outcom...
    method getTotalAmount (line 74) | @Transactional
    method fullName (line 84) | protected String fullName(Employee employee) {

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportRepository.java
  type ReportRepository (line 16) | @RepositoryRestResource(path = "travelExpenseReports")
    method findAll (line 19) | @Override
    method findOne (line 23) | @Override
    method findByEmployeeAndStatusOrderByStatusAsc (line 27) | @PreAuthorize("#employee.email == principal?.username")
    method findByStatus (line 30) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method findBySubmissionDateBetween (line 33) | @PreAuthorize("hasRole('ROLE_ADMIN')")

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportService.java
  class ReportService (line 11) | @Transactional
    method submit (line 23) | @PreAuthorize("#travelExpenseReport.employee.email == principal?.usern...
    method accept (line 29) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') and #travelExpenseReport.emp...
    method reject (line 36) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') and #travelExpenseReport.emp...
    method setStatusOnTravelExpenseReport (line 43) | private Report setStatusOnTravelExpenseReport(Report travelExpenseRepo...

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/Comment.java
  class Comment (line 16) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentEventHandler.java
  class CommentEventHandler (line 8) | @RepositoryEventHandler(value = Comment.class)
    method checkCreateAuthority (line 12) | @HandleBeforeCreate
    method checkUpdateAuthority (line 18) | @HandleBeforeSave
    method checkLinkSaveAuthority (line 24) | @HandleBeforeLinkSave
    method checkLinkDeleteAuthority (line 30) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentRepository.java
  type CommentRepository (line 12) | @RepositoryRestResource(path = "travelExpenseReportComments")
    method findAll (line 15) | @Override
    method findOne (line 19) | @Override
    method delete (line 23) | @Override
    method findByTravelExpenseReportOrderBySubmissionDateAsc (line 27) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #travelExpenseReport.empl...

FILE: src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentWithEmployeeProjection.java
  type CommentWithEmployeeProjection (line 11) | @Projection(types = Comment.class, name = "withEmployee")
    method getId (line 13) | Long getId();
    method getText (line 15) | String getText();
    method getSubmissionDate (line 17) | Date getSubmissionDate();
    method getEmployee (line 19) | Employee getEmployee();

FILE: src/main/java/de/techdev/trackr/domain/employee/login/PrincipalController.java
  class PrincipalController (line 21) | @Controller
    class ReturnValue (line 25) | @Getter
    method principal (line 40) | @RequestMapping(value = "/principal", produces = MediaType.APPLICATION...

FILE: src/main/java/de/techdev/trackr/domain/employee/login/support/SupervisorService.java
  class SupervisorService (line 9) | public class SupervisorService {
    method getSupervisorEmailsAsArray (line 17) | public String[] getSupervisorEmailsAsArray() {
    method getSupervisorEmailsArrayWithout (line 25) | public String[] getSupervisorEmailsArrayWithout(Predicate<String> with...

FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDays.java
  class SickDays (line 15) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysEventHandler.java
  class SickDaysEventHandler (line 7) | @RepositoryEventHandler(value = SickDays.class)
    method checkSaveAuthority (line 14) | @HandleBeforeCreate
    method checkUpdateAuthority (line 20) | @HandleBeforeSave
    method checkDeleteAuthority (line 26) | @HandleBeforeDelete
    method checkLinkUpdateAuthority (line 31) | @HandleBeforeLinkSave
    method checkLinkSaveAuthority (line 37) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysNotifyService.java
  class SickDaysNotifyService (line 13) | public class SickDaysNotifyService {
    method notifySupervisorsAboutNew (line 21) | public void notifySupervisorsAboutNew(SickDays sickDays) {
    method notifySupervisorsAboutUpdate (line 29) | public void notifySupervisorsAboutUpdate(SickDays sickDays) {
    method notifySupervisors (line 37) | protected void notifySupervisors(String subject, String text) {

FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysRepository.java
  type SickDaysRepository (line 16) | @RepositoryRestResource(path = "sickDays")
    method findAll (line 19) | @Override
    method findOne (line 23) | @Override
    method findByEmployee (line 27) | @PreAuthorize("#employee.email == principal?.username")
    method findByStartDateBetweenOrEndDateBetween (line 30) | @PreAuthorize("hasRole('ROLE_ADMIN')")

FILE: src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysWithEmployeeProjection.java
  type SickDaysWithEmployeeProjection (line 11) | @Projection(types = SickDays.class, name = "withEmployee")
    method getId (line 13) | Long getId();
    method getVersion (line 15) | Integer getVersion();
    method getEmployee (line 17) | Employee getEmployee();
    method getStartDate (line 19) | Date getStartDate();
    method getEndDate (line 21) | Date getEndDate();

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/Holiday.java
  class Holiday (line 13) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/HolidayCalculator.java
  class HolidayCalculator (line 17) | public class HolidayCalculator {
    method calculateDifferenceBetweenExcludingHolidaysAndWeekends (line 22) | public Integer calculateDifferenceBetweenExcludingHolidaysAndWeekends(...
    method calculateDifferenceBetweenExcludingHolidaysAndWeekends (line 29) | protected Integer calculateDifferenceBetweenExcludingHolidaysAndWeeken...
    method isWeekendOrHoliday (line 48) | protected boolean isWeekendOrHoliday(LocalDate date, List<LocalDate> h...

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/HolidayRepository.java
  type HolidayRepository (line 14) | public interface HolidayRepository extends CrudRepository<Holiday, Long> {
    method save (line 16) | @Override
    method delete (line 20) | @Override
    method findByFederalStateAndDayBetween (line 24) | List<Holiday> findByFederalStateAndDayBetween(@Param("state") FederalS...

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequest.java
  class VacationRequest (line 16) | @Entity
    type VacationRequestStatus (line 22) | public enum VacationRequestStatus {
    method isApproved (line 59) | @JsonIgnore

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestApproveService.java
  class VacationRequestApproveService (line 17) | @Slf4j
    method approve (line 32) | @Transactional
    method reject (line 48) | @Transactional
    method approveSevenDayOldRequests (line 57) | @Transactional
    method setStatusOnVacationRequest (line 68) | protected VacationRequest setStatusOnVacationRequest(VacationRequest v...

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestController.java
  class VacationRequestController (line 18) | @Controller
    method approve (line 37) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method reject (line 53) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method daysPerEmployeeBetween (line 67) | @PreAuthorize("hasRole('ROLE_ADMIN')")

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestEventHandler.java
  class VacationRequestEventHandler (line 12) | @RepositoryEventHandler(VacationRequest.class)
    method prepareVacationRequest (line 25) | @HandleBeforeCreate
    method afterCreation (line 38) | @HandleAfterCreate
    method authorizeUpdate (line 44) | @HandleBeforeSave
    method authorizeDelete (line 49) | @HandleBeforeDelete
    method denyLinksSave (line 55) | @HandleBeforeLinkSave
    method denyLinks (line 61) | @HandleBeforeLinkDelete
    method employeeCanDeleteRequest (line 73) | public boolean employeeCanDeleteRequest(String username, VacationReque...
    method supervisorCanDeleteRequest (line 78) | public boolean supervisorCanDeleteRequest(String username, VacationReq...

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestRepository.java
  type VacationRequestRepository (line 14) | public interface VacationRequestRepository extends CrudRepository<Vacati...
    method findOne (line 16) | @Override
    method findAll (line 20) | @Override
    method findByEmployeeOrderByStartDateAsc (line 24) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') or principal?.username == #e...
    method findByStatusOrderBySubmissionTimeAsc (line 27) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method findBySubmissionTimeBeforeAndStatus (line 30) | @RestResource(exported = false)
    method findByStartDateBetweenOrEndDateBetweenAndStatus (line 36) | @RestResource(exported = false)
    method findOneWithoutSecurity (line 47) | @RestResource(exported = false)

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestScheduledJobs.java
  class VacationRequestScheduledJobs (line 10) | @Slf4j
    method approveSevenDaysOldVacationRequests (line 16) | @Scheduled(cron = "0 0 4 * * *")

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestWithEmployeeAndApproverProjection.java
  type VacationRequestWithEmployeeAndApproverProjection (line 11) | @Projection(types = VacationRequest.class, name = "withEmployeeAndApprov...
    method getId (line 13) | Long getId();
    method getEmployee (line 15) | Employee getEmployee();
    method getStartDate (line 17) | Date getStartDate();
    method getEndDate (line 19) | Date getEndDate();
    method getNumberOfDays (line 21) | Integer getNumberOfDays();
    method getStatus (line 23) | VacationRequest.VacationRequestStatus getStatus();
    method getApprovalDate (line 25) | Date getApprovalDate();
    method getSubmissionTime (line 27) | Date getSubmissionTime();
    method getApprover (line 29) | Employee getApprover();

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/MailApproveService.java
  class MailApproveService (line 17) | @Slf4j
    method approveOrRejectFromMail (line 31) | public void approveOrRejectFromMail(Message mail) {
    method approveOrReject (line 68) | protected VacationRequest.VacationRequestStatus approveOrReject(String...
    method actualApprove (line 82) | protected void actualApprove(Long vacationRequestId, String supervisor...

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/MessageWrapper.java
  class MessageWrapper (line 12) | class MessageWrapper {
    method MessageWrapper (line 16) | public MessageWrapper(Message message) {
    method extractContentAsString (line 29) | String extractContentAsString() throws IOException, MessagingException {
    method getSender (line 56) | String getSender() throws MessagingException {

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestEmployeeToDaysTotalService.java
  class VacationRequestEmployeeToDaysTotalService (line 17) | public class VacationRequestEmployeeToDaysTotalService {
    method mapVacationRequestsToTotalDays (line 32) | public Map<String, Integer> mapVacationRequestsToTotalDays(Date start,...
    method mapToEmployeesAndSumUp (line 44) | protected Map<String, Integer> mapToEmployeesAndSumUp(List<VacationReq...
    method getVacationDaysBetween (line 56) | protected Integer getVacationDaysBetween(VacationRequest vacationReque...
    method getMinimum (line 69) | protected Date getMinimum(Date a, Date b) {
    method getMaximum (line 76) | protected Date getMaximum(Date a, Date b) {

FILE: src/main/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestNotifyService.java
  class VacationRequestNotifyService (line 12) | public class VacationRequestNotifyService {
    method sendEmailNotification (line 24) | public void sendEmailNotification(VacationRequest request) {
    method statusPastVerb (line 33) | protected String statusPastVerb(VacationRequest.VacationRequestStatus ...
    method getStatusMailText (line 43) | protected String getStatusMailText(VacationRequest request) {
    method notifySupervisors (line 54) | public void notifySupervisors(VacationRequest vacationRequest, UUID uu...

FILE: src/main/java/de/techdev/trackr/domain/employee/worktimetracking/WorkTimeTrackingReminderService.java
  class WorkTimeTrackingReminderService (line 16) | public class WorkTimeTrackingReminderService {
    method remindEmployeesToTrackWorkTimes (line 31) | public void remindEmployeesToTrackWorkTimes(FederalState state) {
    method getReminderMailMessage (line 39) | protected SimpleMailMessage getReminderMailMessage(Employee employee) {

FILE: src/main/java/de/techdev/trackr/domain/project/Project.java
  class Project (line 20) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/project/ProjectEventHandler.java
  class ProjectEventHandler (line 9) | @RepositoryEventHandler(Project.class)
    method checkUpdatePermission (line 13) | @HandleBeforeSave
    method checkCreatePermission (line 18) | @HandleBeforeCreate
    method checkDeletePermission (line 23) | @HandleBeforeDelete
    method checkCompanyPermission (line 28) | @HandleBeforeLinkSave
    method checkLinkDeletePermission (line 33) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/project/ProjectRepository.java
  type ProjectRepository (line 11) | public interface ProjectRepository extends JpaRepository<Project, Long> {
    method findByIdentifier (line 13) | Project findByIdentifier(@Param("identifier") String identifier);
    method findByNameLikeIgnoreCaseOrIdentifierLikeIgnoreCaseOrderByNameAsc (line 15) | List<Project> findByNameLikeIgnoreCaseOrIdentifierLikeIgnoreCaseOrderB...

FILE: src/main/java/de/techdev/trackr/domain/project/ProjectWithCompanyAndDebitorProjection.java
  type ProjectWithCompanyAndDebitorProjection (line 11) | @Projection(types = Project.class, name = "withCompanyAndDebitor")
    method getId (line 13) | Long getId();
    method getVersion (line 15) | Integer getVersion();
    method getIdentifier (line 17) | String getIdentifier();
    method getName (line 19) | String getName();
    method getCompany (line 21) | Company getCompany();
    method getVolume (line 23) | Integer getVolume();
    method getHourlyRate (line 25) | BigDecimal getHourlyRate();
    method getDailyRate (line 27) | BigDecimal getDailyRate();
    method getFixedPrice (line 29) | BigDecimal getFixedPrice();
    method getDebitor (line 31) | Company getDebitor();

FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTime.java
  class BillableTime (line 16) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeController.java
  class BillableTimeController (line 25) | @Controller
    method findEmployeeMappingByProjectAndDateBetween (line 36) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")

FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeEventHandler.java
  class BillableTimeEventHandler (line 9) | @RepositoryEventHandler(BillableTime.class)
    method checkCreateAuthority (line 13) | @HandleBeforeCreate
    method checkUpdateAuthority (line 18) | @HandleBeforeSave
    method checkDeleteAuthority (line 23) | @HandleBeforeDelete
    method checkLinkSaveAuthority (line 28) | @HandleBeforeLinkSave
    method checkLinkDeleteAuthority (line 33) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeRepository.java
  type BillableTimeRepository (line 15) | public interface BillableTimeRepository extends CrudRepository<BillableT...
    method findAll (line 16) | @Override
    method findAll (line 20) | @Override
    method findOne (line 24) | @Override
    method findByProjectAndDateBetweenOrderByDateAsc (line 28) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method findByDateBetween (line 33) | @PreAuthorize("hasRole('ROLE_ADMIN')")

FILE: src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeWithProjectProjection.java
  type BillableTimeWithProjectProjection (line 11) | @Projection(types = BillableTime.class, name = "withProject")
    method getId (line 13) | Long getId();
    method getVersion (line 15) | Integer getVersion();
    method getProject (line 17) | Project getProject();
    method getDate (line 19) | Date getDate();
    method getMinutes (line 21) | Integer getMinutes();

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/ChangeStateService.java
  class ChangeStateService (line 8) | public class ChangeStateService {
    method changeState (line 13) | public Invoice changeState(Invoice invoice, Invoice.InvoiceState invoi...

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/Invoice.java
  class Invoice (line 14) | @Entity
    type InvoiceState (line 20) | public enum InvoiceState {

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceController.java
  class InvoiceController (line 14) | @Controller
    method markAsPaid (line 24) | @RequestMapping(value = "{id}/markPaid", method = RequestMethod.POST)

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceEventHandler.java
  class InvoiceEventHandler (line 15) | @RepositoryEventHandler(Invoice.class)
    method authorizeCreate (line 19) | @HandleBeforeCreate
    method authorizeUpdate (line 26) | @HandleBeforeSave
    method authorizeDelete (line 33) | @HandleBeforeDelete
    method linkSave (line 38) | @HandleBeforeLinkSave
    method setInvoiceStateIfNecessary (line 50) | protected void setInvoiceStateIfNecessary(Invoice invoice) {
    method setDueDateFromTimeForPayment (line 64) | protected void setDueDateFromTimeForPayment(Invoice invoice) {

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceOverdueService.java
  class InvoiceOverdueService (line 16) | @Slf4j
    method markOverdueInvoices (line 26) | @Transactional

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceRepository.java
  type InvoiceRepository (line 17) | public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
    method findOne (line 19) | @Override
    method findAll (line 23) | @Override
    method findByInvoiceState (line 27) | @PreAuthorize("hasRole('ROLE_ADMIN')")
    method findByIdentifierLikeIgnoreCaseAndInvoiceState (line 30) | @PreAuthorize("hasRole('ROLE_ADMIN')")
    method findByDueDateBeforeAndInvoiceState (line 33) | @RestResource(exported = false)
    method findByCreationDateBetween (line 36) | @PreAuthorize("hasRole('ROLE_ADMIN')")

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceScheduledJob.java
  class InvoiceScheduledJob (line 11) | public class InvoiceScheduledJob {
    method markOverdueInvoices (line 16) | @Scheduled(cron = "0 0 1 * * *")

FILE: src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceWithDebitorProjection.java
  type InvoiceWithDebitorProjection (line 12) | @Projection(types = Invoice.class, name = "withDebitor")
    method getId (line 14) | Long getId();
    method getVersion (line 16) | Integer getVersion();
    method getIdentifier (line 18) | String getIdentifier();
    method getCreationDate (line 20) | Date getCreationDate();
    method getInvoiceTotal (line 22) | BigDecimal getInvoiceTotal();
    method getDebitor (line 24) | Company getDebitor();
    method getDueDate (line 26) | Date getDueDate();
    method getInvoiceState (line 28) | Invoice.InvoiceState getInvoiceState();

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/CustomWorkTime.java
  class CustomWorkTime (line 18) | @Getter
    method reduceAndSortWorktimes (line 38) | public static List<CustomWorkTime> reduceAndSortWorktimes(List<CustomW...
    method addOtherWorkTime (line 45) | private CustomWorkTime addOtherWorkTime(CustomWorkTime other) {
    method addComment (line 54) | private void addComment(String comment) {
    method valueOf (line 62) | public static CustomWorkTime valueOf(WorkTime workTime) {
    method compareTo (line 70) | @Override
    method equals (line 75) | @Override
    method hashCode (line 90) | @Override

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/Projections.java
  class Projections (line 13) | public class Projections {
    type WorkTimeWithEmployeeProjection (line 14) | @Projection(types = WorkTime.class, name = "withEmployee")
      method getId (line 16) | Long getId();
      method getVersion (line 18) | Integer getVersion();
      method getEmployee (line 20) | Employee getEmployee();
      method getDate (line 22) | Date getDate();
      method getStartTime (line 24) | Time getStartTime();
      method getEndTime (line 26) | Time getEndTime();
      method getComment (line 28) | String getComment();
    type WorkTimeWithProjectProjection (line 31) | @Projection(types = WorkTime.class, name = "withProject")
      method getId (line 33) | Long getId();
      method getVersion (line 35) | Integer getVersion();
      method getProject (line 37) | Project getProject();
      method getDate (line 39) | Date getDate();
      method getStartTime (line 41) | Time getStartTime();
      method getEndTime (line 43) | Time getEndTime();
      method getComment (line 45) | String getComment();

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTime.java
  class WorkTime (line 17) | @Entity

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeController.java
  class WorkTimeController (line 31) | @Controller
    method findEmployeeMappingByProjectAndDateBetween (line 56) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method getBilledMinutesMapping (line 77) | protected Map<Long, Map<Date, BillableTime>> getBilledMinutesMapping(D...
    method convertStreamOfWorkTimesToMap (line 93) | protected Map<Long, WorkTimeEmployee> convertStreamOfWorkTimesToMap(Li...

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeEmployee.java
  class WorkTimeEmployee (line 19) | @Getter
    method valueOf (line 36) | public static WorkTimeEmployee valueOf(Employee employee, List<CustomW...
    method addBilledMinutes (line 43) | public void addBilledMinutes(Map<Date, BillableTime> dateBillableTimeM...

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeEventHandler.java
  class WorkTimeEventHandler (line 8) | @RepositoryEventHandler(WorkTime.class)
    method checkCreateAuthority (line 12) | @HandleBeforeCreate
    method checkUpdateAuthority (line 17) | @HandleBeforeSave
    method checkDeleteAuthority (line 22) | @HandleBeforeDelete
    method checkUpdateLinkAuthority (line 27) | @HandleBeforeLinkSave
    method checkDeleteLinkAuthority (line 35) | @HandleBeforeLinkDelete

FILE: src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeRepository.java
  type WorkTimeRepository (line 16) | public interface WorkTimeRepository extends CrudRepository<WorkTime, Lon...
    method findAll (line 18) | @Override
    method findAll (line 22) | @Override
    method findOne (line 26) | @Override
    method findByEmployeeAndDateOrderByStartTimeAsc (line 30) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #employee.email == princi...
    method findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc (line 33) | @PreAuthorize("hasRole('ROLE_SUPERVISOR') or #employee.email == princi...
    method findByProjectAndDateBetweenOrderByDateAscStartTimeAsc (line 38) | @PreAuthorize("hasRole('ROLE_SUPERVISOR')")
    method findByDateBetween (line 43) | @PreAuthorize("hasRole('ROLE_ADMIN')")

FILE: src/main/java/de/techdev/trackr/domain/scheduling/LastWorkdayDayOfMonthTrigger.java
  class LastWorkdayDayOfMonthTrigger (line 27) | @Setter
    method getHolidaysForMonth (line 43) | protected List<LocalDate> getHolidaysForMonth(FederalState state, Loca...
    method lastWeekdayInMonth (line 59) | protected LocalDate lastWeekdayInMonth(LocalDate month, List<LocalDate...
    method isWorkday (line 74) | protected boolean isWorkday(LocalDate date, List<LocalDate> holidays) {
    method nextExecutionTime (line 79) | @Override
    method nextExecutionTimeInternal (line 84) | protected LocalDate nextExecutionTimeInternal(TriggerContext triggerCo...

FILE: src/main/java/de/techdev/trackr/domain/scheduling/ScheduledJobsConfiguration.java
  class ScheduledJobsConfiguration (line 30) | @Configuration
    method vacationScheduledJobs (line 34) | @Bean
    method employeeScheduledJob (line 39) | @Bean
    method lastWorkdayDayOfMonthTrigger (line 44) | @Bean
    method invoiceScheduledJob (line 50) | @Bean
    method configureTasks (line 55) | @Override
    method taskExecutor (line 68) | @Bean(destroyMethod = "shutdownNow")
    method createSchedulerSecurityContext (line 78) | private SecurityContext createSchedulerSecurityContext() {

FILE: src/main/java/de/techdev/trackr/domain/translations/TranslationController.java
  class TranslationController (line 23) | @Controller
    method getTranslations (line 34) | @RequestMapping(method = RequestMethod.GET)
    method setLocale (line 48) | @RequestMapping(method = RequestMethod.PUT, produces = MediaType.TEXT_...

FILE: src/main/java/de/techdev/trackr/domain/validation/EndAfterBeginValidator.java
  class EndAfterBeginValidator (line 13) | public class EndAfterBeginValidator implements ConstraintValidator<EndAf...
    method initialize (line 19) | @Override
    method isValid (line 26) | @Override

FILE: src/main/java/de/techdev/trackr/domain/validation/ProjectBelongsToCompanyValidator.java
  class ProjectBelongsToCompanyValidator (line 14) | public class ProjectBelongsToCompanyValidator implements ConstraintValid...
    method initialize (line 20) | @Override
    method isValid (line 27) | @Override

FILE: src/main/java/de/techdev/trackr/util/LocalDateUtil.java
  class LocalDateUtil (line 13) | public final class LocalDateUtil {
    method LocalDateUtil (line 15) | private LocalDateUtil() {
    method fromLocalDate (line 18) | public static Date fromLocalDate(LocalDate date) {
    method fromDate (line 23) | public static LocalDate fromDate(Date date) {

FILE: src/main/resources/data.sql
  type uuid_mapping (line 1) | CREATE TABLE IF NOT EXISTS uuid_mapping (id int, uuid varchar)

FILE: src/main/resources/db/migration/V11__add_travel_expense_report_comments.sql
  type TravelExpenseReportComment (line 1) | create table TravelExpenseReportComment (

FILE: src/main/resources/db/migration/V14__add_uuid_mapping.sql
  type uuid_mapping (line 1) | CREATE TABLE uuid_mapping (

FILE: src/main/resources/db/migration/V15__migrate_credentials.sql
  type Settings (line 13) | CREATE TABLE Settings (

FILE: src/main/resources/db/migration/V1__create_schema.sql
  type Address (line 1) | create table Address (
  type Authority (line 12) | create table Authority (
  type BillableTime (line 19) | create table BillableTime (
  type Company (line 30) | create table Company (
  type ContactPerson (line 39) | create table ContactPerson (
  type Credential (line 51) | create table Credential (
  type Credential_Authority (line 59) | create table Credential_Authority (
  type Employee (line 64) | create table Employee (
  type Holiday (line 80) | create table Holiday (
  type Project (line 88) | create table Project (
  type VacationRequest (line 102) | create table VacationRequest (
  type WorkTime (line 116) | create table WorkTime (
  type TravelExpense (line 128) | create table TravelExpense (
  type TravelExpenseReport (line 141) | create table TravelExpenseReport (

FILE: src/main/resources/db/migration/V4__add_invoices.sql
  type Invoice (line 1) | create table Invoice (

FILE: src/main/resources/db/migration/V8__add_sick_days.sql
  type SickDays (line 1) | create table SickDays (

FILE: src/test/java/de/techdev/test/FlywayTest.java
  class FlywayTest (line 16) | @RunWith(SpringRunner.class)
    class FlywayTestConfig (line 20) | public static class FlywayTestConfig {
      method dataSource (line 22) | @Bean
    method migrationSucceeds (line 32) | @Test

FILE: src/test/java/de/techdev/test/InMemoryOAuth2ResourceServerConfiguration.java
  class InMemoryOAuth2ResourceServerConfiguration (line 14) | @Configuration
    method oauthDataSource (line 18) | @Override
    method tokenStore (line 23) | @Override

FILE: src/test/java/de/techdev/test/TestConstants.java
  class TestConstants (line 3) | public class TestConstants {

FILE: src/test/java/de/techdev/test/TransactionalIntegrationTest.java
  class TransactionalIntegrationTest (line 14) | @SpringBootTest(classes = { Trackr.class })

FILE: src/test/java/de/techdev/test/oauth/OAuthTestExecutionListener.java
  class OAuthTestExecutionListener (line 29) | @Slf4j
    method OAuthTestExecutionListener (line 36) | public OAuthTestExecutionListener() {
    method beforeTestMethod (line 42) | @Override
    method insertOauthTokenIntoStore (line 57) | private void insertOauthTokenIntoStore(TestContext testContext, @Nonnu...
    method getAuthenticationFromAnnotation (line 62) | private Authentication getAuthenticationFromAnnotation(OAuthRequest to...
    method afterTestMethod (line 104) | @Override

FILE: src/test/java/de/techdev/test/rest/AbstractDomainResourceSecurityTest.java
  class AbstractDomainResourceSecurityTest (line 11) | public abstract class AbstractDomainResourceSecurityTest extends Abstrac...
    method getResourceName (line 18) | protected abstract String getResourceName();
    method getJsonEntity (line 20) | private HttpEntity<String> getJsonEntity(String content) {
    method root (line 27) | protected ResponseEntity root() throws Exception {
    method one (line 31) | protected ResponseEntity one(Long id) throws Exception {
    method oneUrl (line 35) | protected ResponseEntity oneUrl(String url) throws Exception {
    method create (line 39) | protected ResponseEntity create(String payload) throws Exception {
    method update (line 43) | protected ResponseEntity update(Long id, String payload) throws Except...
    method updateLink (line 53) | protected ResponseEntity updateLink(Long id, String linkName, String l...
    method updateViaPatch (line 60) | protected ResponseEntity updateViaPatch(Long id, String patch) throws ...
    method remove (line 64) | protected ResponseEntity remove(Long id) throws Exception {
    method removeUrl (line 68) | protected ResponseEntity removeUrl(String url) throws Exception {

FILE: src/test/java/de/techdev/test/rest/AbstractJsonGenerator.java
  class AbstractJsonGenerator (line 12) | public abstract class AbstractJsonGenerator<T, B extends AbstractJsonGen...
    method AbstractJsonGenerator (line 18) | public AbstractJsonGenerator() {
    method start (line 22) | public B start() {
    method apply (line 32) | public final B apply(Consumer<T> function) {
    method build (line 40) | public String build() {
    method getJsonRepresentation (line 47) | protected abstract String getJsonRepresentation(T object);
    method getNewTransientObject (line 53) | protected abstract T getNewTransientObject(int i);
    method getSelf (line 58) | protected abstract B getSelf();
    method reset (line 63) | protected void reset() {

FILE: src/test/java/de/techdev/test/rest/AbstractRestIntegrationTest.java
  class AbstractRestIntegrationTest (line 21) | @RunWith(SpringRunner.class)
    method setUpMvcFields (line 34) | @Before

FILE: src/test/java/de/techdev/test/rest/DomainResourceTestMatchers.java
  class DomainResourceTestMatchers (line 11) | public class DomainResourceTestMatchers {
    method DomainResourceTestMatchers (line 13) | private DomainResourceTestMatchers() {
    type ConsumerWithException (line 20) | @FunctionalInterface
      method accept (line 22) | public void accept(T item) throws Exception;
    class ResultActionsMatcher (line 25) | private static class ResultActionsMatcher extends TypeSafeMatcher<Resp...
      method ResultActionsMatcher (line 29) | protected ResultActionsMatcher(String description, ConsumerWithExcep...
      method matchesSafely (line 34) | @Override
      method describeTo (line 44) | @Override
    method isAccessible (line 53) | public static Matcher<? super ResponseEntity> isAccessible() {
    method isCreated (line 60) | public static Matcher<? super ResponseEntity> isCreated() {
    method isForbidden (line 67) | public static Matcher<? super ResponseEntity> isForbidden() {
    method isUpdated (line 74) | public static Matcher<? super ResponseEntity> isUpdated() {
    method isNoContent (line 81) | public static Matcher<? super ResponseEntity> isNoContent() {
    method isMethodNotAllowed (line 88) | public static Matcher<? super ResponseEntity> isMethodNotAllowed() {

FILE: src/test/java/de/techdev/test/rest/TestRestTemplate.java
  class TestRestTemplate (line 29) | public class TestRestTemplate extends RestTemplate {
    method TestRestTemplate (line 36) | public TestRestTemplate(String token, HttpClientOption... httpClientOp...
    method addAuthentication (line 50) | private void addAuthentication(String token) {
    type HttpClientOption (line 64) | public static enum HttpClientOption {
    class BearerAuthorizationInterceptor (line 78) | private static class BearerAuthorizationInterceptor implements
      method BearerAuthorizationInterceptor (line 83) | public BearerAuthorizationInterceptor(String token) {
      method intercept (line 87) | @Override
    class CustomHttpComponentsClientHttpRequestFactory (line 96) | protected static class CustomHttpComponentsClientHttpRequestFactory ex...
      method CustomHttpComponentsClientHttpRequestFactory (line 103) | public CustomHttpComponentsClientHttpRequestFactory(
      method createHttpContext (line 112) | @Override
      method getRequestConfig (line 119) | protected RequestConfig getRequestConfig() {

FILE: src/test/java/de/techdev/trackr/core/web/converters/DateConverterTest.java
  class DateConverterTest (line 16) | public class DateConverterTest {
    method setUp (line 20) | @Before
    method convert10 (line 25) | @Test
    method convert19 (line 33) | @Test
    method convertNull (line 41) | @Test
    method convertWrongLength (line 47) | @Test(expected = IllegalArgumentException.class)

FILE: src/test/java/de/techdev/trackr/domain/common/FederalStateControllerIntegrationTest.java
  class FederalStateControllerIntegrationTest (line 11) | @OAuthRequest
    method getAllFederalStates (line 14) | @Test

FILE: src/test/java/de/techdev/trackr/domain/common/UuidMapperIntegrationTest.java
  class UuidMapperIntegrationTest (line 17) | @Sql(TestConstants.CREATE_UUID_MAPPING_TABLE_SQL_FILE)
    method insertUuid (line 23) | @Test
    method findUuid (line 29) | @Test
    method deleteUuidByUuid (line 36) | @Test
    method deleteById (line 44) | @Test

FILE: src/test/java/de/techdev/trackr/domain/common/UuidMapperTest.java
  class UuidMapperTest (line 9) | public class UuidMapperTest {
    method setUp (line 13) | @Before
    method extractUuid (line 18) | @Test
    method extractUuidWithPrefix (line 24) | @Test
    method extractUuidWithPrefixAndSuffix (line 30) | @Test

FILE: src/test/java/de/techdev/trackr/domain/company/AddressJsonGenerator.java
  class AddressJsonGenerator (line 8) | public class AddressJsonGenerator extends AbstractJsonGenerator<Address,...
    method getJsonRepresentation (line 10) | @Override
    method getNewTransientObject (line 30) | @Override
    method getSelf (line 43) | @Override

FILE: src/test/java/de/techdev/trackr/domain/company/AddressResourceSecurityTest.java
  class AddressResourceSecurityTest (line 11) | @Sql("address/resourceTest.sql")
    method getResourceName (line 18) | @Override
    method findAllNotExported (line 23) | @Test
    method one (line 29) | @Test
    method createAllowedForAdmin (line 35) | @Test
    method putAllowedForAdmin (line 41) | @Test
    method patchAllowedForAdmin (line 47) | @Test
    method createNotAllowedForSupervisor (line 52) | @Test
    method putForbiddenForSupervisor (line 59) | @Test
    method patchForbiddenForSupervisor (line 66) | @Test
    method deleteNotExported (line 72) | @Test

FILE: src/test/java/de/techdev/trackr/domain/company/CompanyJsonGenerator.java
  class CompanyJsonGenerator (line 8) | public class CompanyJsonGenerator extends AbstractJsonGenerator<Company,...
    method withAddressId (line 12) | public CompanyJsonGenerator withAddressId(Long addressId) {
    method getJsonRepresentation (line 17) | @Override
    method getNewTransientObject (line 39) | @Override
    method getSelf (line 49) | @Override
    method reset (line 54) | @Override

FILE: src/test/java/de/techdev/trackr/domain/company/CompanyRepositoryTest.java
  class CompanyRepositoryTest (line 9) | @Sql("repositoryTest.sql")
    method deleteWithContactPersons (line 16) | @Test
    method deleteWithProject (line 21) | @Test

FILE: src/test/java/de/techdev/trackr/domain/company/CompanyResourceSecurityTest.java
  class CompanyResourceSecurityTest (line 13) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootAccessible (line 25) | @Test
    method one (line 31) | @Test
    method findByNameLikeOrderByNameAsc (line 37) | @Test
    method findByCompanyId (line 44) | @Test
    method postAllowedForAdmin (line 51) | @Test
    method putAllowedForAdmin (line 57) | @Test
    method patchAllowedForAdmin (line 63) | @Test
    method postForbiddenForSupervisor (line 68) | @Test
    method putForbiddenForSupervisor (line 75) | @Test
    method patchForbiddenForSupervisor (line 82) | @Test
    method addContactPersonSupervisor (line 88) | @Test
    method addContactForbiddenForEmployee (line 94) | @Test
    method deleteContactAllowedForSupervisor (line 100) | @Test
    method deleteContactNotAllowedForEmployee (line 106) | @Test
    method deleteAllowedForAdmin (line 112) | @Test
    method deleteForbiddenForSupervisor (line 117) | @Test
    method getAddress (line 123) | @Test

FILE: src/test/java/de/techdev/trackr/domain/company/ContactPersonJsonGenerator.java
  class ContactPersonJsonGenerator (line 8) | public class ContactPersonJsonGenerator extends AbstractJsonGenerator<Co...
    method withCompanyId (line 12) | public ContactPersonJsonGenerator withCompanyId(Long companyId) {
    method getJsonRepresentation (line 17) | @Override
    method getNewTransientObject (line 42) | @Override
    method getSelf (line 56) | @Override
    method reset (line 61) | @Override

FILE: src/test/java/de/techdev/trackr/domain/company/ContactPersonResourceSecurityTest.java
  class ContactPersonResourceSecurityTest (line 16) | @Sql("contactPerson/resourceTest.sql")
    method getResourceName (line 23) | @Override
    method rootAccessible (line 28) | @Test
    method one (line 34) | @Test
    method postAllowedForSupervisor (line 40) | @Test
    method putAllowedForSupervisor (line 46) | @Test
    method putNotAllowedForEmployee (line 52) | @Test
    method patchAllowedForSupervisor (line 59) | @Test
    method patchNotAllowedForEmployee (line 64) | @Test
    method postNotAllowedForEmployee (line 70) | @Test
    method deleteAllowedForSupervisor (line 86) | @Test
    method deleteForbiddenForEmployee (line 91) | @Test
    method updateCompanyForbiddenForEmployee (line 97) | @Test
    method updateCompanyAllowedForSupervisor (line 107) | @Test
    method deleteCompanyForbiddenForSupervisor (line 116) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeControllerSecurityTest.java
  class EmployeeControllerSecurityTest (line 25) | @Sql("resourceTest.sql")
    method setUp (line 32) | @Before
    method updateSelfViaPut (line 43) | @Test
    method updateOtherViaPutIsForbidden (line 55) | @Test
    method accessSelf (line 68) | @Test
    method generateEmployeeJson (line 74) | protected String generateEmployeeJson(SelfEmployee selfEmployee) {

FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeJsonGenerator.java
  class EmployeeJsonGenerator (line 13) | public class EmployeeJsonGenerator extends AbstractJsonGenerator<Employe...
    method getJsonRepresentation (line 15) | @Override
    method getNewTransientObject (line 52) | @Override
    method getSelf (line 68) | @Override

FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeResourceIntegrationTest.java
  class EmployeeResourceIntegrationTest (line 19) | @Sql(scripts = AbstractDomainResourceSecurityTest.EMPTY_DATABASE_FILE, e...
    method creatingAnEmployeeViaRestAlsoCreatesInitialLocaleSettings (line 27) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/EmployeeResourceSecurityTest.java
  class EmployeeResourceSecurityTest (line 15) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootNotAllowedForEmployees (line 25) | @Test
    method rootAllowedForSupervisors (line 31) | @Test
    method oneIsAllowedForSupervisor (line 36) | @Test
    method oneIsAllowedForSelf (line 41) | @Test
    method oneIsForbiddenForOtherEmployee (line 47) | @Test
    method deleteAllowedForAdmins (line 68) | @Test
    method deleteForbiddenForSupervisor (line 84) | @Test
    method getJsonRepresentation (line 89) | protected String getJsonRepresentation(Employee employee) {

FILE: src/test/java/de/techdev/trackr/domain/employee/SelfEmployeeRepositoryTest.java
  class SelfEmployeeRepositoryTest (line 21) | @RunWith(MockitoJUnitRunner.class)
    method setUp (line 33) | @Before
    method updateSelfUpdatesAllAllowedFields (line 44) | @Test
    method updateSelfDoesNotUpdateForbiddenFields (line 59) | @Test
    method updateSelfUpdatesTheAddressWhenItIsNotNull (line 71) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/addressbook/AddressBookControllerSecurityTest.java
  class AddressBookControllerSecurityTest (line 11) | @OAuthRequest
    method rootIsAccessible (line 14) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/addressbook/AddressBookControllerTest.java
  class AddressBookControllerTest (line 13) | public class AddressBookControllerTest {
    method setUp (line 17) | @Before
    method transformEmployees (line 22) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseJsonGenerator.java
  class TravelExpenseJsonGenerator (line 11) | public class TravelExpenseJsonGenerator extends AbstractJsonGenerator<Tr...
    method withReportId (line 15) | public TravelExpenseJsonGenerator withReportId(Long reportId) {
    method getJsonRepresentation (line 20) | @Override
    method getNewTransientObject (line 50) | @Override
    method getSelf (line 63) | @Override
    method reset (line 68) | @Override

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseResourceSecurityTest.java
  class TravelExpenseResourceSecurityTest (line 13) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootNotExported (line 25) | @Test
    method oneForbiddenForOther (line 31) | @Test
    method oneAllowedForSelf (line 37) | @Test
    method createAllowedForSelf (line 42) | @Test
    method createNotAllowedForOther (line 48) | @Test
    method updateAllowedForSelf (line 56) | @Test
    method deletePendingAllowed (line 62) | @Test
    method deleteAcceptedNotAllowed (line 67) | @Test
    method deleteSubmittedNotAllowed (line 72) | @Test
    method changeReportNotAllowed (line 77) | @Test
    method accessTypes (line 83) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportJsonGenerator.java
  class ReportJsonGenerator (line 11) | public class ReportJsonGenerator extends AbstractJsonGenerator<Report, R...
    method withEmployeeId (line 17) | public ReportJsonGenerator withEmployeeId(Long employeeId) {
    method withDebitorId (line 22) | public ReportJsonGenerator withDebitorId(Long debitorId) {
    method withProjectId (line 27) | public ReportJsonGenerator withProjectId(Long projectId) {
    method getJsonRepresentation (line 32) | @Override
    method getNewTransientObject (line 64) | @Override
    method getSelf (line 74) | @Override
    method reset (line 79) | @Override

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportResourceSecurityTest.java
  class ReportResourceSecurityTest (line 15) | @Sql("resourceTest.sql")
    method getResourceName (line 22) | @Override
    method rootNotExported (line 27) | @Test
    method oneAllowedForSupervisor (line 33) | @Test
    method oneNotAllowedForOther (line 39) | @Test
    method oneAllowedForSelf (line 45) | @Test
    method createAllowed (line 50) | @Test
    method updateNotAllowedForSelf (line 56) | @Test
    method updateForbiddenForOther (line 62) | @Test
    method deleteAllowedForOwnerIfPending (line 70) | @Test
    method deleteForbiddenForOwnerIfSubmitted (line 75) | @Test
    method deleteAllowedForAdmin (line 80) | @Test
    method deleteForbiddenForOtherEvenIfPending (line 86) | @Test
    method pdfExport (line 92) | @Test
    method pdfExportAsEmployee (line 99) | @Test
    method updateEmployeeNotAllowedForSupervisor (line 105) | @Test
    method addTravelExpenseAllowedForSelf (line 111) | @Test
    method addTravelExpenseNotAllowedForOther (line 117) | @Test
    method submitNotAllowedForOtherSupervisor (line 123) | @Test
    method approveNotAllowedForOwningSupervisor (line 130) | @Test
    method approveAllowedForSupervisor (line 137) | @Test
    method rejectAllowedForSupervisor (line 144) | @Test
    method findByStatusAllowedForSupervisor (line 151) | @Test
    method findByStatusNotAllowedForEmployee (line 158) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportServiceTest.java
  class ReportServiceTest (line 27) | @RunWith(MockitoJUnitRunner.class)
    method setUp (line 42) | @Before
    method testReject (line 47) | @Test
    method testApprove (line 56) | @Test
    method testSubmit (line 65) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/comment/CommentJsonGenerator.java
  class CommentJsonGenerator (line 11) | public class CommentJsonGenerator extends AbstractJsonGenerator<Comment,...
    method getJsonRepresentation (line 16) | @Override
    method withReportId (line 34) | public CommentJsonGenerator withReportId(Long reportId) {
    method withEmployeeId (line 39) | public CommentJsonGenerator withEmployeeId(Long employeeId) {
    method getNewTransientObject (line 44) | @Override
    method getSelf (line 52) | @Override
    method reset (line 57) | @Override

FILE: src/test/java/de/techdev/trackr/domain/employee/expenses/report/comment/CommentResourceSecurityTest.java
  class CommentResourceSecurityTest (line 11) | @Sql("resourceTest.sql")
    method getResourceName (line 18) | @Override
    method rootNotExported (line 23) | @Test
    method oneNotExported (line 28) | @Test
    method createAllowedForOwningEmployee (line 33) | @Test
    method createAllowedForSupervisor (line 39) | @Test
    method updateForbidden (line 46) | @Test
    method deleteNotExported (line 53) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/login/PrincipalControllerSecurityTest.java
  class PrincipalControllerSecurityTest (line 12) | @OAuthRequest
    method principal (line 17) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/sickdays/SickDaysJsonGenerator.java
  class SickDaysJsonGenerator (line 11) | public class SickDaysJsonGenerator extends AbstractJsonGenerator<SickDay...
    method withEmployeeId (line 15) | public SickDaysJsonGenerator withEmployeeId(Long employeeId) {
    method getJsonRepresentation (line 20) | @Override
    method getNewTransientObject (line 43) | @Override
    method getSelf (line 54) | @Override
    method reset (line 59) | @Override

FILE: src/test/java/de/techdev/trackr/domain/employee/sickdays/SickDaysResourceSecurityTest.java
  class SickDaysResourceSecurityTest (line 12) | @Sql("resourceTest.sql")
    method getResourceName (line 19) | @Override
    method rootIsNotAccessibleForAdmin (line 24) | @Test
    method oneIsAllowedForEmployee (line 30) | @Test
    method oneIsForbiddenForOther (line 35) | @Test
    method createIsAllowedForEmployee (line 41) | @Test
    method createIsForbiddenForOther (line 47) | @Test
    method deleteIsAllowedForAdmin (line 56) | @Test
    method deleteIsForbiddenForSupervisor (line 62) | @Test
    method updateIsAllowedForEmployee (line 68) | @Test
    method updateIsForbiddenForOther (line 74) | @Test
    method findByEmployeeIsAllowedForEmployee (line 83) | @Test
    method findByEmployeeIsForbiddenForOther (line 88) | @Test
    method findByStartDateBetweenOrEndDateBetweenIsAllowedForAdmin (line 96) | @Test
    method findByStartDateBetweenOrEndDateBetweenIsForbiddenForSupervisor (line 104) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/HolidayCalculatorTest.java
  class HolidayCalculatorTest (line 18) | public class HolidayCalculatorTest {
    method setUp (line 22) | @Before
    method calculateDifferenceBetweenExcludingHolidaysAndWeekends (line 27) | @Test
    method calculateDifferenceBetweenExcludingHolidaysAndWeekendsAgain (line 35) | @Test
    method calculateDifferenceBetweenExcludingHolidaysAndWeekendsLastDayWeekend (line 43) | @Test
    method isWeekendOrHolidaySaturday (line 51) | @Test
    method isWeekendOrHolidaySunday (line 58) | @Test
    method isWeekendOrHolidayInList (line 65) | @Test
    method getHolidaysAsDates (line 72) | private List<LocalDate> getHolidaysAsDates() {

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/HolidayResourceTest.java
  class HolidayResourceTest (line 12) | @Sql("holiday/resourceTest.sql")
    method getResourceName (line 17) | @Override
    method rootAccessible (line 22) | @Test
    method oneAccessible (line 27) | @Test
    method createNotExported (line 32) | @Test
    method updateNotExported (line 37) | @Test
    method deleteNotExported (line 42) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestControllerSecurityTest.java
  class VacationRequestControllerSecurityTest (line 20) | @Sql("resourceTest.sql")
    method approveNotAllowedForSupervisorOnOwnVacationRequest (line 26) | @Test
    method approveNotAllowedForEmployees (line 35) | @Test
    method approveAllowedForOtherSupervisor (line 44) | @Test
    method rejectAllowedForSupervisor (line 53) | @Test
    method selfRejectForbiddenForSupervisor (line 62) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestJsonGenerator.java
  class VacationRequestJsonGenerator (line 10) | public class VacationRequestJsonGenerator extends AbstractJsonGenerator<...
    method withEmployeeId (line 15) | public VacationRequestJsonGenerator withEmployeeId(Long employeeId) {
    method withApproverId (line 20) | public VacationRequestJsonGenerator withApproverId(Long approverId) {
    method getJsonRepresentation (line 25) | @Override
    method getNewTransientObject (line 61) | @Override
    method getSelf (line 70) | @Override
    method reset (line 75) | @Override

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestRepositoryTest.java
  class VacationRequestRepositoryTest (line 18) | @Sql("repositoryTest.sql")
    method findBySubmissionTimeBefore (line 25) | @Test
    method findByApprovedBetween (line 32) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestResourceSecurityTest.java
  class VacationRequestResourceSecurityTest (line 14) | @Sql("resourceTest.sql")
    method getResourceName (line 22) | @Override
    method rootNotExported (line 27) | @Test
    method oneAllowedForEmployee (line 32) | @Test
    method oneForbiddenForOther (line 37) | @Test
    method findByEmployeeOrderByStartDateAscAllowedForEmployee (line 43) | @Test
    method findByEmployeeOrderByStartDateAscAllowedForSupervisor (line 49) | @Test
    method findByEmployeeOrderByStartDateAscForbiddenForOther (line 59) | @Test
    method createAllowedForEmployee (line 67) | @Test
    method createForbiddenForSupervisorIfNotOwner (line 73) | @Test
    method updateForbiddenForEmployee (line 80) | @Test
    method updateAllowedForSupervisor (line 86) | @Test
    method updateSelfNotAllowedForSupervisor (line 93) | @Test
    method updateForbiddenForOther (line 105) | @Test
    method deleteAllowedForEmployee (line 113) | @Test
    method deleteApprovedNotAllowedForEmployee (line 118) | @Test
    method deleteRejectedNotAllowedForEmployee (line 124) | @Test
    method deleteAllowedForSupervisor (line 130) | @Test
    method deleteForbiddenForOwningSupervisor (line 136) | @Test
    method deleteForbiddenForOther (line 142) | @Test
    method updateEmployeeIsForbidden (line 148) | @Test
    method updateApproverIsForbidden (line 154) | @Test
    method deleteEmployeeIsForbidden (line 160) | @Test
    method deleteApproverIsForbidden (line 165) | @Test
    method getApprover (line 170) | @Test
    method findByStatusOrderBySubmissionTimeAscForbiddenForEmployee (line 176) | @Test
    method findByStatusOrderBySubmissionTimeAscAllowedForSupervisor (line 182) | @Test
    method daysPerEmployeeBetweenAccessibleForAdmin (line 189) | @Test
    method daysPerEmployeeBetweenForbiddenForSupervisor (line 196) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestScheduledJobsTest.java
  class VacationRequestScheduledJobsTest (line 11) | @RunWith(MockitoJUnitRunner.class)
    method callsTheRightMethod (line 20) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/MailApproveServiceTest.java
  class MailApproveServiceTest (line 10) | public class MailApproveServiceTest {
    method setUp (line 14) | @Before
    method approveOrReject_approve (line 19) | @Test
    method approveOrReject_reject (line 25) | @Test
    method approveOrReject_exception (line 31) | @Test(expected = IllegalStateException.class)

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/MessageWrapperTest.java
  class MessageWrapperTest (line 13) | public class MessageWrapperTest {
    method getSender (line 15) | @Test
    method getSenderReturnsNullWhenNotTechdev (line 23) | @Test
    method getBodyPlaintext (line 31) | @Test
    method getBodyMimeMultipart (line 39) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestEmployeeToDaysTotalServiceTest.java
  class VacationRequestEmployeeToDaysTotalServiceTest (line 17) | public class VacationRequestEmployeeToDaysTotalServiceTest {
    method setUp (line 21) | @Before
    method testGetMinimum (line 26) | @Test
    method testGetMaximum (line 34) | @Test
    method mapEmployeesAndSumUp (line 42) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestNotifyServiceTest.java
  class VacationRequestNotifyServiceTest (line 20) | @RunWith(MockitoJUnitRunner.class)
    method setUp (line 31) | @Before
    method sendNotificationApproved (line 39) | @Test
    method sendNotificationRejected (line 50) | @Test

FILE: src/test/java/de/techdev/trackr/domain/employee/worktimetracking/WorkTimeTrackingReminderServiceIntegrationTest.java
  class WorkTimeTrackingReminderServiceIntegrationTest (line 10) | @ContextConfiguration(classes = {ApiBeansConfiguration.class})
    method remindEmployeesToTrackWorkTimes (line 16) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/ProjectJsonGenerator.java
  class ProjectJsonGenerator (line 9) | public class ProjectJsonGenerator extends AbstractJsonGenerator<Project,...
    method withCompanyId (line 14) | public ProjectJsonGenerator withCompanyId(Long companyId) {
    method withDebitorId (line 19) | public ProjectJsonGenerator withDebitorId(Long debitorId) {
    method getNewTransientObject (line 24) | @Override
    method reset (line 37) | @Override
    method getJsonRepresentation (line 43) | @Override
    method getSelf (line 72) | @Override

FILE: src/test/java/de/techdev/trackr/domain/project/ProjectResourceSecurityTest.java
  class ProjectResourceSecurityTest (line 13) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootAccessible (line 25) | @Test
    method one (line 31) | @Test
    method createAllowedForAdmin (line 37) | @Test
    method updateAllowedForAdmin (line 43) | @Test
    method deleteAllowedForAdmin (line 49) | @Test
    method createForbiddenForSupervisor (line 54) | @Test
    method updateForbiddenForSupervisor (line 61) | @Test
    method deleteForbiddenForSupervisor (line 68) | @Test
    method setCompanyAllowedForAdmin (line 74) | @Test
    method setCompanyForbiddenForSupervisor (line 79) | @Test
    method setDebitorAllowedForAdmin (line 85) | @Test
    method setDebitorForbiddenForSupervisor (line 90) | @Test
    method setWorktimesAllowedForAdmin (line 96) | @Test
    method setWorktimesForbiddenForSupervisor (line 101) | @Test
    method deleteCompanyAllowedForAdmin (line 107) | @Test
    method deleteCompanyForbiddenForSupervisor (line 112) | @Test
    method deleteDebitorAllowedForAdmin (line 118) | @Test
    method deleteDebitorForbiddenForSupervisor (line 123) | @Test
    method deleteWorktimesAllowedForAdmin (line 129) | @Test
    method deleteWorktimesForbiddenForSupervisor (line 134) | @Test
    method getNewTransientObject (line 140) | public Project getNewTransientObject(int i) {

FILE: src/test/java/de/techdev/trackr/domain/project/billtimes/BillableTimeControllerIntegrationTest.java
  class BillableTimeControllerIntegrationTest (line 12) | public class BillableTimeControllerIntegrationTest extends AbstractRestI...
    method findEmployeeMappingByProjectAndDateBetweenForbiddenForEmployee (line 14) | @Test
    method findEmployeeMappingByProjectAndDateBetweenAllowedForSupervisor (line 22) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/billtimes/BillableTimeResourceSecurityTest.java
  class BillableTimeResourceSecurityTest (line 13) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootNotExported (line 25) | @Test
    method oneAllowedForSupervisor (line 30) | @Test
    method oneForbiddenForEmployee (line 36) | @Test
    method createAllowedForSupervisor (line 41) | @Test
    method createForbiddenForEmployee (line 48) | @Test
    method deleteAllowedForSupervisor (line 54) | @Test
    method deleteForbiddenForEmployee (line 60) | @Test
    method updateAllowedForSupervisor (line 65) | @Test
    method updateForbiddenForEmployee (line 75) | @Test
    method deleteEmployeeForbidden (line 82) | @Test
    method deleteProjectForbidden (line 88) | @Test
    method updateEmployeeAllowedForSupervisor (line 94) | @Test
    method updateProjectAllowedForSupervisor (line 100) | @Test
    method updateEmployeeForbiddenForEmployee (line 106) | @Test
    method updateProjectForbiddenForEmployee (line 111) | @Test
    method findByDateBetweenAllowedForAdmin (line 116) | @Test
    method findByDateBetweenForbiddenForSupervisor (line 123) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/billtimes/BillableTimesJsonGenerator.java
  class BillableTimesJsonGenerator (line 12) | public class BillableTimesJsonGenerator extends AbstractJsonGenerator<Bi...
    method withEmployeeId (line 17) | public BillableTimesJsonGenerator withEmployeeId(Long employeeId) {
    method withProjectId (line 22) | public BillableTimesJsonGenerator withProjectId(Long projectId) {
    method getJsonRepresentation (line 27) | @Override
    method getNewTransientObject (line 47) | @Override
    method getSelf (line 56) | @Override
    method reset (line 61) | @Override

FILE: src/test/java/de/techdev/trackr/domain/project/invoice/InvoiceEventHandlerTest.java
  class InvoiceEventHandlerTest (line 13) | public class InvoiceEventHandlerTest {
    method setUp (line 17) | @Before
    method testSetInvoiceStateIfNecessaryOverdue (line 22) | @Test
    method testSetInvoiceStateIfNecessaryOutstanding (line 31) | @Test
    method testSetDueDateFromTimeForPayment (line 40) | @Test
    method testDontSetDueDateFromTimeForPaymentIfItIsFilled (line 52) | @Test
    method testSetDueDateFromTimeForPaymentDontFailOnNoDebitor (line 65) | @Test
    method testSetDueDateFromTimeForPaymentDontFailOnNoTimeForPayment (line 72) | @Test
    method stateGetsSetToOverdueIfTimeForPaymentSetsDueDateInPast (line 82) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/invoice/InvoiceJsonGenerator.java
  class InvoiceJsonGenerator (line 11) | public class InvoiceJsonGenerator extends AbstractJsonGenerator<Invoice,...
    method withDebitorId (line 15) | public InvoiceJsonGenerator withDebitorId(Long debitorId) {
    method getJsonRepresentation (line 20) | @Override
    method getNewTransientObject (line 42) | @Override
    method getSelf (line 53) | @Override
    method reset (line 58) | @Override

FILE: src/test/java/de/techdev/trackr/domain/project/invoice/InvoiceResourceSecurityTest.java
  class InvoiceResourceSecurityTest (line 13) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootIsAccessibleForAdmin (line 25) | @Test
    method rootIsForbiddenForSupervisor (line 30) | @Test
    method oneIsAccessibleForAdmin (line 36) | @Test
    method oneIsForbiddenForSupervisor (line 41) | @Test
    method findByInvoiceStateIsAccessibleForAdmin (line 47) | @Test
    method findByInvoiceStateIsForbiddenForSupervisor (line 52) | @Test
    method findByIdentifierLikeAndInvoiceStateIsAccessibleForAdmin (line 58) | @Test
    method findByIdentifierLikeAndInvoiceStateIsForbiddenForSupervisor (line 63) | @Test
    method findByCreationDateBetweenAccessibleForAdmin (line 69) | @Test
    method findByCreationDateBetweenForbiddenForSupervisor (line 75) | @Test
    method adminCanCreate (line 82) | @Test
    method supervisorCannotCreate (line 88) | @Test
    method adminCanDelete (line 95) | @Test
    method supervisorCannotDelete (line 100) | @Test
    method adminCanSetPaid (line 106) | @Test
    method supervisorCannotSetPaid (line 114) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/CustomWorkTimeTest.java
  class CustomWorkTimeTest (line 15) | public class CustomWorkTimeTest {
    method reduceAndSortWorktimes (line 16) | @Test
    method reduceAndSortWorkTimesPreservesComments (line 24) | @Test
    method createCustomWorkTimes (line 31) | private List<CustomWorkTime> createCustomWorkTimes() throws ParseExcep...
    method customWorkTimeHourCalculation (line 48) | @Test
    method customWorkTimeTransfersTheCommentFromTheWorkTime (line 58) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeControllerSecurityTest.java
  class WorkTimeControllerSecurityTest (line 13) | @OAuthRequest
    method findEmployeeMappingByProjectAndDateBetweenForbiddenForEmployee (line 16) | @Test
    method findEmployeeMappingByProjectAndDateBetweenAllowedForSupervisor (line 23) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeControllerTest.java
  class WorkTimeControllerTest (line 29) | @RunWith(MockitoJUnitRunner.class)
    method convertStreamOfWorkTimesToMap (line 38) | @Test
    method createTestWorktimes (line 51) | private List<WorkTime> createTestWorktimes() throws Exception {

FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeJsonGenerator.java
  class WorkTimeJsonGenerator (line 11) | public class WorkTimeJsonGenerator extends AbstractJsonGenerator<WorkTim...
    method withEmployeeId (line 16) | public WorkTimeJsonGenerator withEmployeeId(Long employeeId) {
    method withProjectId (line 21) | public WorkTimeJsonGenerator withProjectId(Long projectId) {
    method getJsonRepresentation (line 26) | @Override
    method getNewTransientObject (line 52) | @Override
    method getSelf (line 62) | @Override
    method reset (line 67) | @Override

FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeRepositoryTest.java
  class WorkTimeRepositoryTest (line 23) | @Sql("repositoryTest.sql")
    method findByEmployeeAndDateOnlyRespectsDatePart (line 39) | @Test
    method findByEmployeeAndDateBetweenOrderByDateAscStartTimeAsc (line 47) | @Test
    method findByProjectAndDateBetweenOrderByDateAscStartTimeAsc (line 58) | @Test

FILE: src/test/java/de/techdev/trackr/domain/project/worktimes/WorkTimeResourceSecurityTest.java
  class WorkTimeResourceSecurityTest (line 13) | @Sql("resourceTest.sql")
    method getResourceName (line 20) | @Override
    method rootNotExported (line 25) | @Test
    method oneAllowedForOwner (line 30) | @Test
    method oneForbiddenForOther (line 35) | @Test
    method createAllowedForEveryoneIfIsEmployee (line 41) | @Test
    method updateAllowedForOwner (line 47) | @Test
    method updateAllowedForAdmin (line 53) | @Test
    method updateNotAllowedForSupervisor (line 60) | @Test
    method deleteAllowedForOwner (line 67) | @Test
    method deleteAllowedForAdmin (line 72) | @Test
    method deleteNotAllowedForSupervisor (line 78) | @Test
    method updateEmployeeNotAllowed (line 91) | @Test
    method deleteEmployeeNotAllowed (line 97) | @Test
    method deleteProjectNotAllowed (line 103) | @Test
    method updateProjectAllowedForOwner (line 109) | @Test
    method updateProjectAllowedForAdmin (line 114) | @Test
    method updateProjectForbiddenForSupervisor (line 120) | @Test
    method findByEmployeeAndDateOrderByStartTimeAscAllowedForOwner (line 126) | @Test
    method findByEmployeeAndDateOrderByStartTimeAscAllowedForSupervisor (line 133) | @Test
    method findByDateBetweenAllowedForAdmin (line 141) | @Test
    method findByDateBetweenForbiddenForSupervisor (line 149) | @Test
    method findByEmployeeAndDateBetweenOrderByDateAscStartTimeAscAllowedForOwner (line 157) | @Test
    method findByEmployeeAndDateBetweenOrderByDateAscStartTimeAscAllowedForSupervisor (line 164) | @Test
    method findByEmployeeAndDateBetweenOrderByDateAscStartTimeAscForbiddenForOther (line 177) | @Test
    method findByEmployeeAndDateOrderByStartTimeAscForbiddenForOther (line 191) | @Test
    method findByProjectAndDateBetweenOrderByDateAscStartTimeAscAllowedForSupervisor (line 200) | @Test
    method findByProjectAndDateBetweenOrderByDateAscStartTimeAscForbiddenForEmployee (line 208) | @Test

FILE: src/test/java/de/techdev/trackr/domain/scheduling/LastWorkdayDayOfMonthTriggerTest.java
  class LastWorkdayDayOfMonthTriggerTest (line 29) | @RunWith(MockitoJUnitRunner.class)
    method setUp (line 40) | @Before
    method lastDayIsNotWeekend (line 53) | @Test
    method lastDayIsSunday (line 60) | @Test
    method lastDayIsSaturday (line 67) | @Test
    method lastDayIsMondayAndHoliday (line 74) | @Test
    method lastDayIsMondayAndHolidayAndTheFridayIsAHolidayToo (line 83) | @Test
    method triggerThisMonthIfLastScheduledTimeIsNull (line 92) | @Test
    method dontTriggerTwiceAMonth (line 99) | @Test
    method getTriggerContextWithLastScheduledExecutionTime (line 106) | protected TriggerContext getTriggerContextWithLastScheduledExecutionTi...

FILE: src/test/java/de/techdev/trackr/domain/translations/TranslationControllerSecurityTest.java
  class TranslationControllerSecurityTest (line 11) | @OAuthRequest
    method testGetTranslationsIsAccessible (line 14) | @Test

FILE: src/test/java/de/techdev/trackr/domain/translations/TranslationControllerTest.java
  class TranslationControllerTest (line 21) | @RunWith(MockitoJUnitRunner.class)
    method testGetTranslations (line 33) | @Test
    method testSetTranslations (line 41) | @Test

FILE: src/test/resources/de/techdev/trackr/domain/tableUuidMapping.sql
  type uuid_mapping (line 1) | CREATE TABLE IF NOT EXISTS uuid_mapping (
Condensed preview — 245 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (611K chars).
[
  {
    "path": ".gitignore",
    "chars": 183,
    "preview": "# OS\napplication.pid\n\n# gradle / Maven\ntarget/\nbuild/\n.gradle/\n\n# IDEA\n.idea/\n*.iml\n*.ipr\natlassian-ide-plugin.xml\n\n# Ec"
  },
  {
    "path": "LICENSE",
    "chars": 1100,
    "preview": "The MIT License\n\nCopyright (c) 2014 techdev Solutions UG http://techdev.de\n\nPermission is hereby granted, free of charge"
  },
  {
    "path": "README.md",
    "chars": 5824,
    "preview": "trackr backend\n==============\n\nWhat is it?\n-------------\ntrackr is an application to track petty much everything that is"
  },
  {
    "path": "application.yaml",
    "chars": 1891,
    "preview": "spring:\n    jpa:\n        hibernate:\n            naming:\n                physical-strategy: org.hibernate.boot.model.nami"
  },
  {
    "path": "build.gradle",
    "chars": 1920,
    "preview": "plugins {\n    id 'org.springframework.boot' version '1.5.19.RELEASE'\n}\n\napply plugin: 'java'\napply plugin: 'jacoco'\n\nsou"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Fri Jan 23 09:44:57 CET 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "src/main/java/de/techdev/trackr/Trackr.java",
    "chars": 1343,
    "preview": "package de.techdev.trackr;\n\nimport org.springframework.boot.ApplicationPid;\nimport org.springframework.boot.SpringApplic"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/mail/GMailConfiguration.java",
    "chars": 2399,
    "preview": "package de.techdev.trackr.core.mail;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springfr"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/mail/MailService.java",
    "chars": 209,
    "preview": "package de.techdev.trackr.core.mail;\n\nimport org.springframework.mail.SimpleMailMessage;\n\n/**\n * @author Moritz Schulze\n"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/mail/support/AsyncMailService.java",
    "chars": 962,
    "preview": "package de.techdev.trackr.core.mail.support;\n\nimport de.techdev.trackr.core.mail.MailService;\nimport org.springframework"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/mail/support/NoOpJavaMailSender.java",
    "chars": 2137,
    "preview": "package de.techdev.trackr.core.mail.support;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annot"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/pdf/HtmlPdfConverter.java",
    "chars": 1187,
    "preview": "package de.techdev.trackr.core.pdf;\n\nimport com.itextpdf.text.DocumentException;\nimport org.xhtmlrenderer.pdf.ITextRende"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/pdf/PdfCreationException.java",
    "chars": 198,
    "preview": "package de.techdev.trackr.core.pdf;\n\n/**\n * @author Moritz Schulze\n */\npublic class PdfCreationException extends Excepti"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/pdf/PdfRenderer.java",
    "chars": 1156,
    "preview": "package de.techdev.trackr.core.pdf;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotati"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/pdf/ThymeleafRenderer.java",
    "chars": 1670,
    "preview": "package de.techdev.trackr.core.pdf;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.thymeleaf.TemplateEngine;\nimport org.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/security/AuthorityService.java",
    "chars": 359,
    "preview": "package de.techdev.trackr.core.security;\n\nimport java.util.Collection;\n\n/**\n * Access GrantedAuthorities for Employees a"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/security/InMemoryAuthorityService.java",
    "chars": 861,
    "preview": "package de.techdev.trackr.core.security;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techdev.trackr.do"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/security/InMemorySecurityConfiguration.java",
    "chars": 2416,
    "preview": "package de.techdev.trackr.core.security;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techdev.trackr.do"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/security/MethodSecurityConfiguration.java",
    "chars": 2559,
    "preview": "package de.techdev.trackr.core.security;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.spri"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/security/OAuth2AuthorityService.java",
    "chars": 903,
    "preview": "package de.techdev.trackr.core.security;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.spri"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/security/OAuth2ResourceServerConfiguration.java",
    "chars": 3703,
    "preview": "package de.techdev.trackr.core.security;\n\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.spri"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/api/ApiRepositoryRestConfiguration.java",
    "chars": 3555,
    "preview": "package de.techdev.trackr.core.web.api;\n\nimport de.techdev.trackr.core.web.converters.DateConverter;\nimport de.techdev.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/api/ApiWebMvcConfiguration.java",
    "chars": 2606,
    "preview": "package de.techdev.trackr.core.web.api;\n\nimport de.techdev.trackr.core.web.converters.DateConverter;\nimport de.techdev.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/api/ArgumentResolverPagingAndSortingTemplateVariables.java",
    "chars": 2679,
    "preview": "package de.techdev.trackr.core.web.api;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.dat"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/api/ExceptionHandlers.java",
    "chars": 2366,
    "preview": "package de.techdev.trackr.core.web.api;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.anno"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/api/JsonMappingHandlerExceptionResolver.java",
    "chars": 3897,
    "preview": "package de.techdev.trackr.core.web.api;\n\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxm"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/api/RepositoryEntityLinksWithoutProjection.java",
    "chars": 2567,
    "preview": "package de.techdev.trackr.core.web.api;\n\nimport org.springframework.data.repository.support.Repositories;\nimport org.spr"
  },
  {
    "path": "src/main/java/de/techdev/trackr/core/web/converters/DateConverter.java",
    "chars": 1724,
    "preview": "package de.techdev.trackr.core.web.converters;\n\nimport org.springframework.core.convert.converter.Converter;\n\nimport jav"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/ApiBeansConfiguration.java",
    "chars": 3162,
    "preview": "package de.techdev.trackr.domain;\n\nimport de.techdev.trackr.core.pdf.PdfRenderer;\nimport de.techdev.trackr.domain.common"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/common/EmployeeSettingsLocaleResolver.java",
    "chars": 3573,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport de.techdev.trackr.domain.employee.Settings;\nimport de.techdev.trackr.do"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/common/FederalState.java",
    "chars": 905,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\n\n/**\n * @author Moritz Sch"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/common/FederalStateController.java",
    "chars": 718,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.stereoty"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/common/UuidMapper.java",
    "chars": 2704,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport javax.s"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/Address.java",
    "chars": 559,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.hibernate.validator.co"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/AddressEventHandler.java",
    "chars": 1253,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.rest.core.annotation.HandleBeforeCreate;\nimpo"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/AddressRepository.java",
    "chars": 876,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.domain.Page;\nimport org.springframework.data."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/Company.java",
    "chars": 1516,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport de.techd"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/CompanyEventHandler.java",
    "chars": 1054,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.rest.core.annotation.*;\nimport org.springfram"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/CompanyRepository.java",
    "chars": 572,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.spri"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/CompanyWithAddressAndContactPersonsProjection.java",
    "chars": 506,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.rest.core.config.Projection;\n\nimport java.uti"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/ContactPerson.java",
    "chars": 799,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.hibernate.validator.co"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/ContactPersonEventHandler.java",
    "chars": 1142,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.rest.core.annotation.*;\nimport org.springfram"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/company/ContactPersonRepository.java",
    "chars": 227,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport org.springframework.data.repository.CrudRepository;\n\n/**\n * @author Mo"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/Employee.java",
    "chars": 2564,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport de.tech"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/EmployeeController.java",
    "chars": 1779,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.sp"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/EmployeeEventHandler.java",
    "chars": 1291,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.sp"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/EmployeeRepository.java",
    "chars": 1841,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport org.springframew"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/EmployeeScheduledJob.java",
    "chars": 933,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport de.techdev.track"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/Projections.java",
    "chars": 889,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport de.techdev.track"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/SelfEmployee.java",
    "chars": 1171,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.trackr.domain.company.Address;\nimport lombok.Getter;\nimpor"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/SelfEmployeeRepository.java",
    "chars": 1714,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.trackr.domain.company.Address;\nimport de.techdev.trackr.do"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/Settings.java",
    "chars": 697,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.hibernate.validator.c"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/SettingsRepository.java",
    "chars": 499,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport org.springframework.data.repository.Repository;\nimport org.springfram"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/addressbook/AddressBookController.java",
    "chars": 2307,
    "preview": "package de.techdev.trackr.domain.employee.addressbook;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.tec"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/addressbook/EmployeeForAddressBookDTO.java",
    "chars": 1222,
    "preview": "package de.techdev.trackr.domain.employee.addressbook;\n\nimport de.techdev.trackr.domain.company.Address;\nimport de.techd"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpense.java",
    "chars": 1196,
    "preview": "package de.techdev.trackr.domain.employee.expenses;\n\nimport de.techdev.trackr.domain.employee.expenses.reports.Report;\ni"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseEventHandler.java",
    "chars": 2387,
    "preview": "package de.techdev.trackr.domain.employee.expenses;\n\nimport de.techdev.trackr.domain.employee.expenses.reports.Report;\ni"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseRepository.java",
    "chars": 594,
    "preview": "package de.techdev.trackr.domain.employee.expenses;\n\nimport org.springframework.data.repository.CrudRepository;\nimport o"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseTypeController.java",
    "chars": 756,
    "preview": "package de.techdev.trackr.domain.employee.expenses;\n\nimport org.springframework.http.MediaType;\nimport org.springframewo"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/Projections.java",
    "chars": 1620,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.domain.company.Company;\nimport de."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/Report.java",
    "chars": 1600,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.domain.company.Company;\nimport de."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportController.java",
    "chars": 3782,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.core.pdf.PdfCreationException;\nimp"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportEventHandler.java",
    "chars": 2373,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport o"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportNotifyService.java",
    "chars": 3457,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.core.mail.MailService;\nimport de.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportRepository.java",
    "chars": 1505,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport o"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/ReportService.java",
    "chars": 2441,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport d"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/Comment.java",
    "chars": 846,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports.comments;\n\nimport de.techdev.trackr.domain.employee.Employee;"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentEventHandler.java",
    "chars": 1016,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports.comments;\n\nimport org.springframework.data.rest.core.annotati"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentRepository.java",
    "chars": 1097,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports.comments;\n\nimport de.techdev.trackr.domain.employee.expenses."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/expenses/reports/comments/CommentWithEmployeeProjection.java",
    "chars": 451,
    "preview": "package de.techdev.trackr.domain.employee.expenses.reports.comments;\n\nimport de.techdev.trackr.domain.employee.Employee;"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/login/PrincipalController.java",
    "chars": 2255,
    "preview": "package de.techdev.trackr.domain.employee.login;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techdev.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/login/support/SupervisorService.java",
    "chars": 1018,
    "preview": "package de.techdev.trackr.domain.employee.login.support;\n\nimport de.techdev.trackr.core.security.AuthorityService;\nimpor"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDays.java",
    "chars": 774,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysEventHandler.java",
    "chars": 1383,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimpo"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysNotifyService.java",
    "chars": 1978,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport de.techdev.trackr.core.mail.MailService;\nimport de.techdev.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysRepository.java",
    "chars": 1445,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport org.sprin"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/sickdays/SickDaysWithEmployeeProjection.java",
    "chars": 459,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport org.sprin"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/Holiday.java",
    "chars": 523,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport lombok."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/HolidayCalculator.java",
    "chars": 2102,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport de.tech"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/HolidayRepository.java",
    "chars": 765,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport org.spr"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequest.java",
    "chars": 1342,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport de.techd"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestApproveService.java",
    "chars": 3690,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.common.UuidMapper;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestController.java",
    "chars": 3221,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.employee.vacation.support.VacationR"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestEventHandler.java",
    "chars": 3405,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.common.UuidMapper;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestRepository.java",
    "chars": 2299,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport org.sprin"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestScheduledJobs.java",
    "chars": 623,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/VacationRequestWithEmployeeAndApproverProjection.java",
    "chars": 645,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport org.sprin"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/support/MailApproveService.java",
    "chars": 4249,
    "preview": "package de.techdev.trackr.domain.employee.vacation.support;\n\nimport de.techdev.trackr.domain.common.UuidMapper;\nimport d"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/support/MessageWrapper.java",
    "chars": 2417,
    "preview": "package de.techdev.trackr.domain.employee.vacation.support;\n\nimport javax.mail.Address;\nimport javax.mail.Message;\nimpor"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestEmployeeToDaysTotalService.java",
    "chars": 3487,
    "preview": "package de.techdev.trackr.domain.employee.vacation.support;\n\nimport de.techdev.trackr.domain.employee.vacation.HolidayCa"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/vacation/support/VacationRequestNotifyService.java",
    "chars": 2954,
    "preview": "package de.techdev.trackr.domain.employee.vacation.support;\n\nimport de.techdev.trackr.core.mail.MailService;\nimport de.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/employee/worktimetracking/WorkTimeTrackingReminderService.java",
    "chars": 1814,
    "preview": "package de.techdev.trackr.domain.employee.worktimetracking;\n\nimport de.techdev.trackr.core.mail.MailService;\nimport de.t"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/Project.java",
    "chars": 1509,
    "preview": "package de.techdev.trackr.domain.project;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport de.techd"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/ProjectEventHandler.java",
    "chars": 1001,
    "preview": "package de.techdev.trackr.domain.project;\n\nimport org.springframework.data.rest.core.annotation.*;\nimport org.springfram"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/ProjectRepository.java",
    "chars": 520,
    "preview": "package de.techdev.trackr.domain.project;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.spri"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/ProjectWithCompanyAndDebitorProjection.java",
    "chars": 620,
    "preview": "package de.techdev.trackr.domain.project;\n\nimport de.techdev.trackr.domain.company.Company;\nimport org.springframework.d"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTime.java",
    "chars": 954,
    "preview": "package de.techdev.trackr.domain.project.billtimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeController.java",
    "chars": 2054,
    "preview": "package de.techdev.trackr.domain.project.billtimes;\n\nimport de.techdev.trackr.domain.project.Project;\nimport org.springf"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeEventHandler.java",
    "chars": 1076,
    "preview": "package de.techdev.trackr.domain.project.billtimes;\n\nimport org.springframework.data.rest.core.annotation.*;\nimport org."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeRepository.java",
    "chars": 1286,
    "preview": "package de.techdev.trackr.domain.project.billtimes;\n\nimport de.techdev.trackr.domain.project.Project;\nimport org.springf"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/billtimes/BillableTimeWithProjectProjection.java",
    "chars": 459,
    "preview": "package de.techdev.trackr.domain.project.billtimes;\n\nimport de.techdev.trackr.domain.project.Project;\nimport org.springf"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/ChangeStateService.java",
    "chars": 437,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n "
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/Invoice.java",
    "chars": 1085,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport de.techdev.trackr.domain.company.Company;\nimport lombok.Getter"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceController.java",
    "chars": 1098,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceEventHandler.java",
    "chars": 2446,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport de.techdev.trackr.domain.company.Company;\nimport de.techdev.tr"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceOverdueService.java",
    "chars": 1164,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.fa"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceRepository.java",
    "chars": 1454,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport org.springframework.data.domain.Page;\nimport org.springframewo"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceScheduledJob.java",
    "chars": 533,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/invoice/InvoiceWithDebitorProjection.java",
    "chars": 590,
    "preview": "package de.techdev.trackr.domain.project.invoice;\n\nimport de.techdev.trackr.domain.company.Company;\nimport org.springfra"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/CustomWorkTime.java",
    "chars": 2858,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.time.Durat"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/Projections.java",
    "chars": 965,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTime.java",
    "chars": 992,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeController.java",
    "chars": 5099,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeEmployee.java",
    "chars": 2008,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeEventHandler.java",
    "chars": 1517,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport org.sprin"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/project/worktimes/WorkTimeRepository.java",
    "chars": 2278,
    "preview": "package de.techdev.trackr.domain.project.worktimes;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport de.techde"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/scheduling/LastWorkdayDayOfMonthTrigger.java",
    "chars": 3771,
    "preview": "package de.techdev.trackr.domain.scheduling;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport de.techdev.tra"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/scheduling/ScheduledJobsConfiguration.java",
    "chars": 3483,
    "preview": "package de.techdev.trackr.domain.scheduling;\n\nimport de.techdev.trackr.domain.common.FederalState;\nimport de.techdev.tra"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/translations/TranslationController.java",
    "chars": 2569,
    "preview": "package de.techdev.trackr.domain.translations;\n\nimport de.techdev.trackr.domain.employee.Settings;\nimport de.techdev.tra"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/validation/EndAfterBeginValidator.java",
    "chars": 1793,
    "preview": "package de.techdev.trackr.domain.validation;\n\nimport de.techdev.trackr.domain.validation.constraints.EndAfterBegin;\nimpo"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/validation/ProjectBelongsToCompanyValidator.java",
    "chars": 1664,
    "preview": "package de.techdev.trackr.domain.validation;\n\nimport de.techdev.trackr.domain.company.Company;\nimport de.techdev.trackr."
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/validation/constraints/EndAfterBegin.java",
    "chars": 866,
    "preview": "package de.techdev.trackr.domain.validation.constraints;\n\nimport de.techdev.trackr.domain.validation.EndAfterBeginValida"
  },
  {
    "path": "src/main/java/de/techdev/trackr/domain/validation/constraints/ProjectBelongsToCompany.java",
    "chars": 902,
    "preview": "package de.techdev.trackr.domain.validation.constraints;\n\nimport de.techdev.trackr.domain.validation.ProjectBelongsToCom"
  },
  {
    "path": "src/main/java/de/techdev/trackr/util/LocalDateUtil.java",
    "chars": 745,
    "preview": "package de.techdev.trackr.util;\n\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\ni"
  },
  {
    "path": "src/main/resources/META-INF/mail-integration.xml",
    "chars": 2047,
    "preview": "<beans:beans xmlns=\"http://www.springframework.org/schema/integration\"\n             xmlns:xsi=\"http://www.w3.org/2001/XM"
  },
  {
    "path": "src/main/resources/banner.txt",
    "chars": 4379,
    "preview": "                                                                                                   \n                    "
  },
  {
    "path": "src/main/resources/data.sql",
    "chars": 54876,
    "preview": "CREATE TABLE IF NOT EXISTS uuid_mapping (id int, uuid varchar);\n\n/* EMPLOYEE ADDRESSES */\nINSERT INTO address (id, versi"
  },
  {
    "path": "src/main/resources/db/migration/V10__modify_travel_expense_add_comment.sql",
    "chars": 61,
    "preview": "alter table TravelExpense\n  add column comment varchar(255);\n"
  },
  {
    "path": "src/main/resources/db/migration/V11__add_travel_expense_report_comments.sql",
    "chars": 459,
    "preview": "create table TravelExpenseReportComment (\n  id int8 not null,\n  submissionDate timestamp,\n  text varchar(255),\n  employe"
  },
  {
    "path": "src/main/resources/db/migration/V12__modify_company_add_time_for_payment.sql",
    "chars": 53,
    "preview": "alter table Company\n  add column timeForPayment int4;"
  },
  {
    "path": "src/main/resources/db/migration/V13__modify_travel_expense_reports_add_debitor_and_project.sql",
    "chars": 556,
    "preview": "alter table TravelExpenseReport\n  add column debitor_id int8 not null default 0;\n\nupdate TravelExpenseReport set debitor"
  },
  {
    "path": "src/main/resources/db/migration/V14__add_uuid_mapping.sql",
    "chars": 69,
    "preview": "CREATE TABLE uuid_mapping (\n  id int8 not null,\n  uuid varchar(40)\n);"
  },
  {
    "path": "src/main/resources/db/migration/V15__migrate_credentials.sql",
    "chars": 816,
    "preview": "ALTER TABLE Employee\n  ADD COLUMN email VARCHAR(255);\n\nALTER TABLE Employee\n    ADD UNIQUE (email);\n\n-- transfer the ema"
  },
  {
    "path": "src/main/resources/db/migration/V16__add_holidays_2015.sql",
    "chars": 3445,
    "preview": "insert into holiday (id, day, name, federalstate) values (167, '2014-12-24', 'Weihnachten', 'BADEN_WUERTTEMBERG');\ninser"
  },
  {
    "path": "src/main/resources/db/migration/V17__add_holidays_2015.sql",
    "chars": 24538,
    "preview": "-- 2015 - Baden Württemberg\ninsert into holiday (id, day, name, federalstate) values (199, '2015-01-01', 'Neujahr', 'BAD"
  },
  {
    "path": "src/main/resources/db/migration/V18__expense_paid_marker.sql",
    "chars": 64,
    "preview": "ALTER TABLE travelexpense ADD COLUMN paid BOOLEAN DEFAULT false;"
  },
  {
    "path": "src/main/resources/db/migration/V19__add_employee_address.sql",
    "chars": 155,
    "preview": "ALTER TABLE employee ADD COLUMN address_id int8;\nALTER TABLE employee ADD CONSTRAINT employee_contact_address FOREIGN KE"
  },
  {
    "path": "src/main/resources/db/migration/V1__create_schema.sql",
    "chars": 5353,
    "preview": "    create table Address (\n        id int8 not null,\n        city varchar(255),\n        country varchar(255),\n        ho"
  },
  {
    "path": "src/main/resources/db/migration/V20__add_employe_deleted.sql",
    "chars": 71,
    "preview": "ALTER TABLE employee ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "src/main/resources/db/migration/V2__add_roles.sql",
    "chars": 588,
    "preview": "insert into employee (id, version, firstName, lastName, title, hourlyCostRate, salary, federalState) values (0, 0, 'admi"
  },
  {
    "path": "src/main/resources/db/migration/V3__add_holidays_2014.sql",
    "chars": 18952,
    "preview": "-- CLean-up\ndelete from holiday;\n\n-- 2014 - Baden Württemberg\ninsert into holiday (id, day, name, federalstate) values ("
  },
  {
    "path": "src/main/resources/db/migration/V4__add_invoices.sql",
    "chars": 373,
    "preview": "create table Invoice (\n    id int8 not null,\n    creationDate date,\n    dueDate date,\n    identifier varchar(255),\n    i"
  },
  {
    "path": "src/main/resources/db/migration/V5__modify_vacationrequests.sql",
    "chars": 71,
    "preview": "alter table VacationRequest\n\talter column approvalDate type timestamp;\n"
  },
  {
    "path": "src/main/resources/db/migration/V6__modify_travel_expense_reports_and_contact_persons.sql",
    "chars": 131,
    "preview": "alter table TravelExpenseReport\n  add column submissionDate timestamp;\n\nalter table ContactPerson\n  add column roles var"
  },
  {
    "path": "src/main/resources/db/migration/V7__update_travel_expense_report_submission_date_from_travel_expenses.sql",
    "chars": 178,
    "preview": "UPDATE TravelExpenseReport ter\n\tSET submissionDate = (\n\t\tSELECT MAX(te.submissionDate)\n\t\tFROM TravelExpense te\n\t\tWHERE t"
  },
  {
    "path": "src/main/resources/db/migration/V8__add_sick_days.sql",
    "chars": 261,
    "preview": "create table SickDays (\n    id int8 not null,\n    endDate date,\n    startDate date,\n    version int4,\n    employee_id in"
  },
  {
    "path": "src/main/resources/db/migration/V9__modify_travel_expense_report.sql",
    "chars": 252,
    "preview": "alter table TravelExpenseReport\n  add column approvalDate timestamp;\n\nalter table TravelExpenseReport\n  add column appro"
  },
  {
    "path": "src/main/resources/i18n/trackr-de.json",
    "chars": 9363,
    "preview": "{\n    \"AUTHORITY\": {\n        \"ROLE_ADMIN\": \"Administrator\",\n        \"ROLE_SUPERVISOR\": \"Supervisor\",\n        \"ROLE_EMPLO"
  },
  {
    "path": "src/main/resources/i18n/trackr-en.json",
    "chars": 9117,
    "preview": "{\n    \"AUTHORITY\": {\n        \"ROLE_ADMIN\": \"Administrator\",\n        \"ROLE_SUPERVISOR\": \"Supervisor\",\n        \"ROLE_EMPLO"
  },
  {
    "path": "src/main/resources/i18n/validation/messages_de.properties",
    "chars": 136,
    "preview": "validation.date.endAfterBegin=Start darf nicht nach Ende sein\nvalidation.company.projectBelongsTo=Das Projekt muss zu de"
  },
  {
    "path": "src/main/resources/i18n/validation/messages_en.properties",
    "chars": 134,
    "preview": "validation.date.endAfterBegin=Start must not be after end.\nvalidation.company.projectBelongsTo=The project must belong t"
  },
  {
    "path": "src/main/resources/logback-console.xml",
    "chars": 900,
    "preview": "<configuration>\n\n    <property name=\"charset\" value=\"UTF-8\"/>\n    <property name=\"pattern\" value=\"%d{HH:mm:ss.SSS} [%thr"
  },
  {
    "path": "src/main/resources/logback-file.xml",
    "chars": 1458,
    "preview": "<configuration>\n\n    <property name=\"charset\" value=\"UTF-8\"/>\n    <property name=\"pattern\" value=\"%d{HH:mm:ss.SSS} [%thr"
  },
  {
    "path": "src/main/resources/pdfTemplates/travel-expenses/report.html",
    "chars": 26055,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:th=\"http://www."
  },
  {
    "path": "src/test/java/de/techdev/test/FlywayTest.java",
    "chars": 1116,
    "preview": "package de.techdev.test;\n\nimport org.flywaydb.core.Flyway;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimpor"
  },
  {
    "path": "src/test/java/de/techdev/test/InMemoryOAuth2ResourceServerConfiguration.java",
    "chars": 857,
    "preview": "package de.techdev.test;\n\nimport de.techdev.trackr.core.security.OAuth2ResourceServerConfiguration;\nimport org.springfra"
  },
  {
    "path": "src/test/java/de/techdev/test/TestConstants.java",
    "chars": 321,
    "preview": "package de.techdev.test;\n\npublic class TestConstants {\n\n    /**\n     * Path to the SQL file that creates the uuid mappin"
  },
  {
    "path": "src/test/java/de/techdev/test/TransactionalIntegrationTest.java",
    "chars": 704,
    "preview": "package de.techdev.test;\n\nimport de.techdev.trackr.Trackr;\nimport org.junit.runner.RunWith;\nimport org.springframework.b"
  },
  {
    "path": "src/test/java/de/techdev/test/oauth/OAuthRequest.java",
    "chars": 585,
    "preview": "package de.techdev.test.oauth;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport stati"
  },
  {
    "path": "src/test/java/de/techdev/test/oauth/OAuthTestExecutionListener.java",
    "chars": 4337,
    "preview": "package de.techdev.test.oauth;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.annotation.AnnotationU"
  },
  {
    "path": "src/test/java/de/techdev/test/rest/AbstractDomainResourceSecurityTest.java",
    "chars": 3134,
    "preview": "package de.techdev.test.rest;\n\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpMethod;\ni"
  },
  {
    "path": "src/test/java/de/techdev/test/rest/AbstractJsonGenerator.java",
    "chars": 1891,
    "preview": "package de.techdev.test.rest;\n\nimport javax.json.Json;\nimport javax.json.stream.JsonGeneratorFactory;\nimport java.util.f"
  },
  {
    "path": "src/test/java/de/techdev/test/rest/AbstractRestIntegrationTest.java",
    "chars": 1663,
    "preview": "package de.techdev.test.rest;\n\nimport de.techdev.test.InMemoryOAuth2ResourceServerConfiguration;\nimport de.techdev.test."
  },
  {
    "path": "src/test/java/de/techdev/test/rest/DomainResourceTestMatchers.java",
    "chars": 3188,
    "preview": "package de.techdev.test.rest;\n\nimport org.hamcrest.Description;\nimport org.hamcrest.Matcher;\nimport org.hamcrest.TypeSaf"
  },
  {
    "path": "src/test/java/de/techdev/test/rest/TestRestTemplate.java",
    "chars": 4606,
    "preview": "package de.techdev.test.rest;\n\nimport org.apache.http.client.config.CookieSpecs;\nimport org.apache.http.client.config.Re"
  },
  {
    "path": "src/test/java/de/techdev/trackr/core/web/converters/DateConverterTest.java",
    "chars": 1423,
    "preview": "package de.techdev.trackr.core.web.converters;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.text.Simple"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/common/FederalStateControllerIntegrationTest.java",
    "chars": 667,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstrac"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/common/UuidMapperIntegrationTest.java",
    "chars": 1615,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport de.techdev.test.TestConstants;\nimport de.techdev.test.TransactionalInte"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/common/UuidMapperTest.java",
    "chars": 1079,
    "preview": "package de.techdev.trackr.domain.common;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.hamcrest.Co"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/AddressJsonGenerator.java",
    "chars": 1439,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\n\nimport javax.json.stream."
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/AddressResourceSecurityTest.java",
    "chars": 2374,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstra"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/CompanyJsonGenerator.java",
    "chars": 1646,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\n\nimport javax.json.stream."
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/CompanyRepositoryTest.java",
    "chars": 816,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.TransactionalIntegrationTest;\nimport de.techdev.test.r"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/CompanyResourceSecurityTest.java",
    "chars": 4217,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstra"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/ContactPersonJsonGenerator.java",
    "chars": 1926,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\n\nimport javax.json.stream."
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/company/ContactPersonResourceSecurityTest.java",
    "chars": 4323,
    "preview": "package de.techdev.trackr.domain.company;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstra"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/EmployeeControllerSecurityTest.java",
    "chars": 4262,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstr"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/EmployeeJsonGenerator.java",
    "chars": 2653,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\nimport de.techdev.trackr."
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/EmployeeResourceIntegrationTest.java",
    "chars": 1965,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstr"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/EmployeeResourceSecurityTest.java",
    "chars": 4021,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest.Abstr"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/SelfEmployeeRepositoryTest.java",
    "chars": 2958,
    "preview": "package de.techdev.trackr.domain.employee;\n\nimport de.techdev.trackr.domain.company.Address;\nimport de.techdev.trackr.do"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/addressbook/AddressBookControllerSecurityTest.java",
    "chars": 672,
    "preview": "package de.techdev.trackr.domain.employee.addressbook;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.tes"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/addressbook/AddressBookControllerTest.java",
    "chars": 945,
    "preview": "package de.techdev.trackr.domain.employee.addressbook;\n\nimport de.techdev.trackr.domain.employee.Employee;\nimport org.ju"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseJsonGenerator.java",
    "chars": 2425,
    "preview": "package de.techdev.trackr.domain.employee.expenses;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\n\nimport javax.js"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/TravelExpenseResourceSecurityTest.java",
    "chars": 2785,
    "preview": "package de.techdev.trackr.domain.employee.expenses;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.r"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportJsonGenerator.java",
    "chars": 2360,
    "preview": "package de.techdev.trackr.domain.employee.expenses.report;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\nimport de"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportResourceSecurityTest.java",
    "chars": 6237,
    "preview": "package de.techdev.trackr.domain.employee.expenses.report;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/report/ReportServiceTest.java",
    "chars": 2772,
    "preview": "package de.techdev.trackr.domain.employee.expenses.report;\n\nimport de.techdev.trackr.domain.employee.EmployeeRepository;"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/report/comment/CommentJsonGenerator.java",
    "chars": 1875,
    "preview": "package de.techdev.trackr.domain.employee.expenses.report.comment;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\ni"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/expenses/report/comment/CommentResourceSecurityTest.java",
    "chars": 2005,
    "preview": "package de.techdev.trackr.domain.employee.expenses.report.comment;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/login/PrincipalControllerSecurityTest.java",
    "chars": 846,
    "preview": "package de.techdev.trackr.domain.employee.login;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.rest"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/sickdays/SickDaysJsonGenerator.java",
    "chars": 1909,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\nimport de.techde"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/sickdays/SickDaysResourceSecurityTest.java",
    "chars": 4168,
    "preview": "package de.techdev.trackr.domain.employee.sickdays;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.r"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/vacation/HolidayCalculatorTest.java",
    "chars": 2964,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.time.L"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/vacation/HolidayResourceTest.java",
    "chars": 1388,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.test.oauth.OAuthRequest;\nimport de.techdev.test.r"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestControllerSecurityTest.java",
    "chars": 4092,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.test.TestConstants;\nimport de.techdev.test.oauth."
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestJsonGenerator.java",
    "chars": 2771,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.test.rest.AbstractJsonGenerator;\n\nimport javax.js"
  },
  {
    "path": "src/test/java/de/techdev/trackr/domain/employee/vacation/VacationRequestRepositoryTest.java",
    "chars": 1719,
    "preview": "package de.techdev.trackr.domain.employee.vacation;\n\nimport de.techdev.test.TransactionalIntegrationTest;\nimport de.tech"
  }
]

// ... and 45 more files (download for full content)

About this extraction

This page contains the full source code of the techdev-solutions/trackr-backend GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 245 files (553.9 KB), approximately 151.7k tokens, and a symbol index with 1087 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!