diff --git a/backend/src/main/java/de/hft/geotime/controllers/RecordController.java b/backend/src/main/java/de/hft/geotime/controllers/RecordController.java new file mode 100644 index 0000000..88bfb28 --- /dev/null +++ b/backend/src/main/java/de/hft/geotime/controllers/RecordController.java @@ -0,0 +1,60 @@ +package de.hft.geotime.controllers; + +import de.hft.geotime.entities.RecordType; +import de.hft.geotime.entities.TimeRecord; +import de.hft.geotime.entities.TimetrackAccount; +import de.hft.geotime.repositories.RecordRepository; +import de.hft.geotime.repositories.TimetrackAccountRepository; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; +import java.util.Optional; + +@RestController +public class RecordController { + + private final RecordRepository recordRepository; + private final TimetrackAccountRepository accountRepository; + + public RecordController(RecordRepository recordRepository, TimetrackAccountRepository accountRepository) { + this.recordRepository = recordRepository; + this.accountRepository = accountRepository; + } + + @GetMapping("/track") + public ResponseEntity track(@RequestParam String account, Authentication authentication) { + if (account == null || account.isEmpty()) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + TimetrackAccount selectedAccount = accountRepository.findByUser_UsernameAndName(authentication.getName(), account); + + if (selectedAccount == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + Page entires = recordRepository.findAllByEnddateIsNull(null); + Optional collect = entires.stream() + .filter(timeRecord -> timeRecord.getAccount().equals(selectedAccount)) + .findFirst(); + + if (collect.isPresent()) { + collect.get().setEnddate(LocalDateTime.now()); + recordRepository.save(collect.get()); + return new ResponseEntity<>(collect.get(), HttpStatus.OK); + } else { + TimeRecord newRecord = new TimeRecord(); + newRecord.setType(RecordType.PAID); + newRecord.setStartdate(LocalDateTime.now()); + newRecord.setAccount(accountRepository.findByUser_UsernameAndName(authentication.getName(), account)); + recordRepository.save(newRecord); + return new ResponseEntity<>(newRecord, HttpStatus.CREATED); + } + } +} diff --git a/backend/src/main/java/de/hft/geotime/entities/TimeRecord.java b/backend/src/main/java/de/hft/geotime/entities/TimeRecord.java index 8a54422..ef08904 100644 --- a/backend/src/main/java/de/hft/geotime/entities/TimeRecord.java +++ b/backend/src/main/java/de/hft/geotime/entities/TimeRecord.java @@ -5,8 +5,8 @@ import lombok.Data; import lombok.NoArgsConstructor; import javax.persistence.*; -import java.time.Duration; -import java.util.Date; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; @Data @NoArgsConstructor @@ -17,11 +17,24 @@ public class TimeRecord { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; - @OneToOne(fetch = FetchType.LAZY) + + @ManyToOne private TimetrackAccount account; - private Date startdate; - private Date enddate; - private Duration time; + + @Column(columnDefinition = "TIMESTAMP") + private LocalDateTime startdate; + + @Column(columnDefinition = "TIMESTAMP") + private LocalDateTime enddate; + private RecordType type; + public long getDuration() { + if (enddate == null) { + return 0; + } else { + return startdate.until(enddate, ChronoUnit.MINUTES); + } + } + } diff --git a/backend/src/main/java/de/hft/geotime/entities/TimetrackAccount.java b/backend/src/main/java/de/hft/geotime/entities/TimetrackAccount.java index 9ee6191..bdec014 100644 --- a/backend/src/main/java/de/hft/geotime/entities/TimetrackAccount.java +++ b/backend/src/main/java/de/hft/geotime/entities/TimetrackAccount.java @@ -1,5 +1,6 @@ package de.hft.geotime.entities; +import com.fasterxml.jackson.annotation.JsonBackReference; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -18,6 +19,7 @@ public class TimetrackAccount { private double revenue; private String name; private String description; + @ManyToOne private TimetrackUser user; diff --git a/backend/src/main/java/de/hft/geotime/entities/TimetrackUser.java b/backend/src/main/java/de/hft/geotime/entities/TimetrackUser.java index d3b6b8c..46447cb 100644 --- a/backend/src/main/java/de/hft/geotime/entities/TimetrackUser.java +++ b/backend/src/main/java/de/hft/geotime/entities/TimetrackUser.java @@ -1,6 +1,7 @@ package de.hft.geotime.entities; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonManagedReference; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -28,12 +29,9 @@ public class TimetrackUser { private String lastname; - @OneToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.EAGER) private Role role; - @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL) - private List accounts; - @ManyToOne private Location location; diff --git a/backend/src/main/java/de/hft/geotime/entities/projections/RecordOverviewProjection.java b/backend/src/main/java/de/hft/geotime/entities/projections/RecordOverviewProjection.java new file mode 100644 index 0000000..a92c091 --- /dev/null +++ b/backend/src/main/java/de/hft/geotime/entities/projections/RecordOverviewProjection.java @@ -0,0 +1,27 @@ +package de.hft.geotime.entities.projections; + +import de.hft.geotime.entities.TimeRecord; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.rest.core.config.Projection; + +import java.time.LocalDateTime; + +@Projection(name = "overview", types = TimeRecord.class) +public interface RecordOverviewProjection { + + LocalDateTime getStartdate(); + + LocalDateTime getEnddate(); + + long getDuration(); + + @Value("#{target.type.name()}") + String getType(); + + @Value("#{target.account.name}") + String getAccount(); + + @Value("#{target.account.user.username}") + String getUsername(); + +} diff --git a/backend/src/main/java/de/hft/geotime/repositories/RecordRepository.java b/backend/src/main/java/de/hft/geotime/repositories/RecordRepository.java index 765b6e9..71c04f6 100644 --- a/backend/src/main/java/de/hft/geotime/repositories/RecordRepository.java +++ b/backend/src/main/java/de/hft/geotime/repositories/RecordRepository.java @@ -1,11 +1,15 @@ package de.hft.geotime.repositories; import de.hft.geotime.entities.TimeRecord; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; +import org.springframework.format.annotation.DateTimeFormat; -import java.util.List; +import java.time.LocalDateTime; @RepositoryRestResource( path = "records", @@ -14,4 +18,37 @@ import java.util.List; ) public interface RecordRepository extends PagingAndSortingRepository { + @RestResource(rel = "allBetween", path = "allBetween") + Page findAllByStartdateBetween( + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime start, + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime end, + Pageable pageable + ); + + @RestResource(rel = "allBetweenAndUser", path = "allBetweenAndUser") + Page findAllByStartdateBetweenAndAccount_User_Username( + LocalDateTime start, + LocalDateTime end, + String username, + Pageable pageable + ); + + @RestResource(rel = "allForUser", path = "allForUser") + Page findAllByAccount_User_Username(String username, Pageable pageable); + + @RestResource(rel = "allForUserAndAccount", path = "allForUserAndAccount") + Page findAllByAccount_User_UsernameAndAccount_Name(String username, String account, Pageable pageable); + + @RestResource(rel = "allFrom", path = "allFrom") + Page findAllByStartdateGreaterThanEqual( + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime date, + Pageable pageable + ); + + @Query("SELECT record from TimeRecord record where record.account.user.username = :#{principal} AND record.startdate > (current_date-1)") + Page today(Pageable pageable); + + @RestResource(rel = "openEntries", path = "openEntries") + Page findAllByEnddateIsNull(Pageable pageable); + } diff --git a/backend/src/main/java/de/hft/geotime/repositories/TimetrackAccountRepository.java b/backend/src/main/java/de/hft/geotime/repositories/TimetrackAccountRepository.java index 81075a5..df69761 100644 --- a/backend/src/main/java/de/hft/geotime/repositories/TimetrackAccountRepository.java +++ b/backend/src/main/java/de/hft/geotime/repositories/TimetrackAccountRepository.java @@ -3,6 +3,7 @@ package de.hft.geotime.repositories; import de.hft.geotime.entities.TimetrackAccount; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; @RepositoryRestResource( path = "accounts", @@ -11,4 +12,7 @@ import org.springframework.data.rest.core.annotation.RepositoryRestResource; ) public interface TimetrackAccountRepository extends PagingAndSortingRepository { + @RestResource(rel = "findByUsernameAndName", path = "findByUsernameAndName") + TimetrackAccount findByUser_UsernameAndName(String username, String account); + } diff --git a/backend/src/main/java/de/hft/geotime/security/SecurityConfiguration.java b/backend/src/main/java/de/hft/geotime/security/SecurityConfiguration.java new file mode 100644 index 0000000..4d86016 --- /dev/null +++ b/backend/src/main/java/de/hft/geotime/security/SecurityConfiguration.java @@ -0,0 +1,14 @@ +package de.hft.geotime.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.spel.spi.EvaluationContextExtension; + +@Configuration +class SecurityConfiguration { + + @Bean + EvaluationContextExtension securityExtension() { + return new SecurityEvaluationContextExtension(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/hft/geotime/security/SecurityEvaluationContextExtension.java b/backend/src/main/java/de/hft/geotime/security/SecurityEvaluationContextExtension.java new file mode 100644 index 0000000..5e42052 --- /dev/null +++ b/backend/src/main/java/de/hft/geotime/security/SecurityEvaluationContextExtension.java @@ -0,0 +1,21 @@ +package de.hft.geotime.security; + +import org.springframework.data.spel.spi.EvaluationContextExtension; +import org.springframework.security.access.expression.SecurityExpressionRoot; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +public class SecurityEvaluationContextExtension implements EvaluationContextExtension { + + @Override + public String getExtensionId() { + return "security"; + } + + @Override + public SecurityExpressionRoot getRootObject() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return new SecurityExpressionRoot(authentication) { + }; + } +} \ No newline at end of file diff --git a/backend/src/main/resources/data.sql b/backend/src/main/resources/data.sql index 3f9bdf3..84c0f16 100644 --- a/backend/src/main/resources/data.sql +++ b/backend/src/main/resources/data.sql @@ -20,8 +20,15 @@ INSERT INTO timetrack_user (id, firstname, lastname, password, username, role_id (3, 'Tim', 'Zieger' ,'$2y$10$pYGHZhoaelceImO7aIN4nOkWJBp.oqNGFYaRAonHkYF4u9ljqPelC', 'ziti', 1, 1), (4, 'Simon', 'Kellner' ,'$2y$10$Puzm/Nr/Dyq3nQxlkXGIfubS5JPtXJSOf2e6mrQ6HhVYQN9YiQQsC', 'kesi', 1, 1); -INSERT INTO timetrack_account (description, `name`, revenue, user_id) VALUES - ('Gleitzeit Marcel', 'Primary Marcel', 16.0, 1), - ('Festgeld Marcel', 'Secondary Marcel', 25.0, 1); +INSERT INTO timetrack_account (id, description, `name`, revenue, user_id) VALUES + (1, 'Gleitzeit Marcel', 'Primary', 16.0, 1), + (2, 'Festgeld Marcel', 'Secondary', 25.0, 1), + (3, 'Festgeld Tim', 'Primary', 25.0, 3); + +INSERT INTO time_record (id, enddate, startdate, `type`, account_id) VALUES + (1, '2020-05-10 16:00:00', '2020-05-10 12:00:00', 0, 1), + (2, '2020-05-09 16:00:00', '2020-05-09 12:00:00', 1, 1), + (3, '2020-05-20 16:00:00', '2020-05-20 00:00:00', 1, 2), + (4, '2020-05-11 16:00:00', '2020-05-11 12:00:00', 1, 3); SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file