diff --git a/src/main/java/de/iwomm/propify_api/controller/ProjectController.java b/src/main/java/de/iwomm/propify_api/controller/ProjectController.java new file mode 100644 index 0000000..a7524d1 --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/controller/ProjectController.java @@ -0,0 +1,89 @@ +package de.iwomm.propify_api.controller; + +import de.iwomm.propify_api.dto.ProjectDTO; +import de.iwomm.propify_api.dto.ProjectStatsDTO; +import de.iwomm.propify_api.dto.PropertyDTO; +import de.iwomm.propify_api.entity.Project; +import de.iwomm.propify_api.entity.Property; +import de.iwomm.propify_api.service.ProjectService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/v1/projects") +public class ProjectController { + private ProjectService projectService; + + public ProjectController(ProjectService projectService) { + this.projectService = projectService; + } + + @GetMapping + @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") + public ResponseEntity getAll() { + List projectDTOs = projectService.toDTOs(projectService.findAll()); + + return ResponseEntity + .ok(projectDTOs); + } + + @GetMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'USER')") + public ResponseEntity getById(@PathVariable UUID id) { + try { + ProjectDTO projectDTO = projectService.toDTO(projectService.findById(id).orElseThrow(EntityNotFoundException::new)); + + return ResponseEntity + .ok(projectDTO); + } catch (EntityNotFoundException e) { + return ResponseEntity + .notFound() + .build(); + } catch (Exception e) { + return ResponseEntity + .internalServerError() + .build(); + } + } + + @GetMapping("/stats") + @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") + public ResponseEntity getProjectStats() { + + ProjectStatsDTO projectStatsDTO = this.projectService.getStatsDTO(); + + List projectDTOs = projectService.toDTOs(projectService.findAll()); + + return ResponseEntity + .ok(projectStatsDTO); + } + + + @PostMapping + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity create(@RequestBody ProjectDTO projectDTO) { + try { + Project newItem = projectService.save(projectDTO); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{id}") + .buildAndExpand(newItem.getId()) + .toUri(); + + return ResponseEntity + .created(location) + .body(newItem); + } catch (Exception e) { + return ResponseEntity + .internalServerError() + .build(); + } + } +} diff --git a/src/main/java/de/iwomm/propify_api/controller/PropertyController.java b/src/main/java/de/iwomm/propify_api/controller/PropertyController.java index 269751f..8415579 100644 --- a/src/main/java/de/iwomm/propify_api/controller/PropertyController.java +++ b/src/main/java/de/iwomm/propify_api/controller/PropertyController.java @@ -35,7 +35,7 @@ public class PropertyController { @GetMapping @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") - public ResponseEntity getAllProperties() { + public ResponseEntity getAll() { List propertiesDTO = propertyService.toDTOs(propertyService.findAll()); return ResponseEntity @@ -44,7 +44,7 @@ public class PropertyController { @GetMapping("/{id}") @PreAuthorize("hasAnyRole('ADMIN', 'USER')") - public ResponseEntity getPropertyById(@PathVariable UUID id) { + public ResponseEntity getById(@PathVariable UUID id) { try { return ResponseEntity .ok(propertyService.findById(id).orElseThrow(EntityNotFoundException::new)); @@ -107,7 +107,7 @@ public class PropertyController { @PostMapping @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity createProperty(@RequestBody PropertyDTO propertyDTO) { + public ResponseEntity create(@RequestBody PropertyDTO propertyDTO) { try { Property newItem = propertyService.saveDTO(propertyDTO); @@ -128,7 +128,7 @@ public class PropertyController { @PatchMapping("/{id}") @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity updateProperty(@PathVariable UUID id, @RequestBody PropertyDTO propertyDTO) { + public ResponseEntity update(@PathVariable UUID id, @RequestBody PropertyDTO propertyDTO) { try { return ResponseEntity .ok(propertyService.update(id, propertyDTO)); @@ -145,7 +145,7 @@ public class PropertyController { @DeleteMapping("/{id}") @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity deleteProperty(@PathVariable UUID id) { + public ResponseEntity delete(@PathVariable UUID id) { try { propertyService.deleteById(id); @@ -165,7 +165,7 @@ public class PropertyController { @PostMapping("/bulk-delete") @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity deleteProperties(@RequestBody BulkDeletePropertyIdsDTO propertiesDTO) { + public ResponseEntity deleteMany(@RequestBody BulkDeletePropertyIdsDTO propertiesDTO) { try { propertyService.deleteByIds(propertiesDTO); diff --git a/src/main/java/de/iwomm/propify_api/dto/ProjectDTO.java b/src/main/java/de/iwomm/propify_api/dto/ProjectDTO.java new file mode 100644 index 0000000..5cd538f --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/dto/ProjectDTO.java @@ -0,0 +1,135 @@ +package de.iwomm.propify_api.dto; + +import com.fasterxml.jackson.annotation.JsonBackReference; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.UUID; + +public class ProjectDTO { + private UUID id; + private String name; + private String eventNumber; + private String description; + private String projectType; + private String status; + private UUID propertyId; + private int amountRequested; + private LocalDate startDate; + private LocalDate endDate; + private PropertyDTO property; + private LocalDateTime createdAt; + + public ProjectDTO(UUID id, String name, String eventNumber, String description, String projectType, + String status, UUID propertyId, int amountRequested, + LocalDate startDate, LocalDate endDate, LocalDateTime createdAt, PropertyDTO propertyDTO) { + this.id = id; + this.name = name; + this.eventNumber = eventNumber; + this.description = description; + this.projectType = projectType; + this.status = status; + this.propertyId = propertyId; + this.amountRequested = amountRequested; + this.startDate = startDate; + this.endDate = endDate; + this.createdAt = createdAt; + this.property = propertyDTO; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEventNumber() { + return eventNumber; + } + + public void setEventNumber(String eventNumber) { + this.eventNumber = eventNumber; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getProjectType() { + return projectType; + } + + public void setProjectType(String projectType) { + this.projectType = projectType; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public UUID getPropertyId() { + return propertyId; + } + + public void setPropertyId(UUID propertyId) { + this.propertyId = propertyId; + } + + public int getAmountRequested() { + return amountRequested; + } + + public void setAmountRequested(int amountRequested) { + this.amountRequested = amountRequested; + } + + public LocalDate getStartDate() { + return startDate; + } + + public void setStartDate(LocalDate startDate) { + this.startDate = startDate; + } + + public LocalDate getEndDate() { + return endDate; + } + + public void setEndDate(LocalDate endDate) { + this.endDate = endDate; + } + + public PropertyDTO getProperty() { + return property; + } + + public void setProperty(PropertyDTO property) { + this.property = property; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/src/main/java/de/iwomm/propify_api/dto/ProjectStatsDTO.java b/src/main/java/de/iwomm/propify_api/dto/ProjectStatsDTO.java new file mode 100644 index 0000000..f281ded --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/dto/ProjectStatsDTO.java @@ -0,0 +1,47 @@ +package de.iwomm.propify_api.dto; + +public class ProjectStatsDTO { + private long projectsCountTotal; + private long projectsCountActive; + private long projectsCountCompleted; + private long amountRequestedInTotal; + + public ProjectStatsDTO() { + this.projectsCountTotal = 0; + this.projectsCountActive = 0; + this.projectsCountCompleted = 0; + this.amountRequestedInTotal = 0; + } + + public long getProjectsCountTotal() { + return projectsCountTotal; + } + + public void setProjectsCountTotal(long projectsCountTotal) { + this.projectsCountTotal = projectsCountTotal; + } + + public long getProjectsCountActive() { + return projectsCountActive; + } + + public void setProjectsCountActive(long projectsCountActive) { + this.projectsCountActive = projectsCountActive; + } + + public long getProjectsCountCompleted() { + return projectsCountCompleted; + } + + public void setProjectsCountCompleted(long projectsCountCompleted) { + this.projectsCountCompleted = projectsCountCompleted; + } + + public long getAmountRequestedInTotal() { + return amountRequestedInTotal; + } + + public void setAmountRequestedInTotal(long amountRequestedInTotal) { + this.amountRequestedInTotal = amountRequestedInTotal; + } +} diff --git a/src/main/java/de/iwomm/propify_api/entity/Project.java b/src/main/java/de/iwomm/propify_api/entity/Project.java new file mode 100644 index 0000000..4c2e343 --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/entity/Project.java @@ -0,0 +1,166 @@ +package de.iwomm.propify_api.entity; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.UUID; + +@Entity +public class Project { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(nullable = false) + private UUID id; + + private String name; + private String eventNumber; + private String description; + private String projectType; + private String status; + + @Column(nullable = false) + private int amountRequested; + + @Column(nullable = false) + private LocalDate startDate; + + @Column(nullable = false) + private LocalDate endDate; + + @Column(nullable = false) + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + @ManyToOne(cascade = CascadeType.PERSIST, optional = false) + @JoinColumn(name = "property_id", nullable = false) + @JsonManagedReference + private Property property; + + public Project() { + this.amountRequested = 0; + } + + public Project(String name, String eventNumber, String description, String projectType, + String status, Property property, int amountRequested, + LocalDate startDate, LocalDate endDate) { + this.name = name; + this.eventNumber = eventNumber; + this.description = description; + this.projectType = projectType; + this.status = status; + this.property = property; + this.amountRequested = amountRequested; + this.startDate = startDate; + this.endDate = endDate; + } + + @PrePersist + protected void onCreate() { + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } + + @PreUpdate + protected void onUpdate() { + this.updatedAt = LocalDateTime.now(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEventNumber() { + return eventNumber; + } + + public void setEventNumber(String eventNumber) { + this.eventNumber = eventNumber; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getProjectType() { + return projectType; + } + + public void setProjectType(String projectType) { + this.projectType = projectType; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public int getAmountRequested() { + return amountRequested; + } + + public void setAmountRequested(int amountRequested) { + this.amountRequested = amountRequested; + } + + public Property getProperty() { + return property; + } + + public void setProperty(Property property) { + this.property = property; + } + + public LocalDate getStartDate() { + return startDate; + } + + public void setStartDate(LocalDate startDate) { + this.startDate = startDate; + } + + public LocalDate getEndDate() { + return endDate; + } + + public void setEndDate(LocalDate endDate) { + this.endDate = endDate; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/src/main/java/de/iwomm/propify_api/entity/Property.java b/src/main/java/de/iwomm/propify_api/entity/Property.java index 0dc27a9..73fbbe5 100644 --- a/src/main/java/de/iwomm/propify_api/entity/Property.java +++ b/src/main/java/de/iwomm/propify_api/entity/Property.java @@ -1,8 +1,10 @@ package de.iwomm.propify_api.entity; +import com.fasterxml.jackson.annotation.JsonBackReference; import jakarta.persistence.*; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -41,6 +43,19 @@ public class Property { private LocalDateTime updatedAt; + @OneToMany(mappedBy = "property", cascade = CascadeType.PERSIST, orphanRemoval = true) + @OrderBy("createdAt") + @JsonBackReference + private List projects = new ArrayList<>(); + + public List getProjects() { + return projects; + } + + public void setProjects(List projects) { + this.projects = projects; + } + public List getAttachments() { return attachments; } diff --git a/src/main/java/de/iwomm/propify_api/repository/ProjectRepository.java b/src/main/java/de/iwomm/propify_api/repository/ProjectRepository.java new file mode 100644 index 0000000..bb31f09 --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/repository/ProjectRepository.java @@ -0,0 +1,18 @@ +package de.iwomm.propify_api.repository; + +import de.iwomm.propify_api.entity.Project; +import de.iwomm.propify_api.entity.Property; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Optional; +import java.util.UUID; + +public interface ProjectRepository extends JpaRepository { + public Optional findByName(String name); + + @Query("SELECT SUM(p.amountRequested) FROM Project p") + long sumAmountRequested(); + + long countProjectByStatus(String status); +} diff --git a/src/main/java/de/iwomm/propify_api/service/ProjectService.java b/src/main/java/de/iwomm/propify_api/service/ProjectService.java new file mode 100644 index 0000000..d5ae421 --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/service/ProjectService.java @@ -0,0 +1,100 @@ +package de.iwomm.propify_api.service; + +import de.iwomm.propify_api.dto.ProjectDTO; +import de.iwomm.propify_api.dto.ProjectStatsDTO; +import de.iwomm.propify_api.dto.PropertyDTO; +import de.iwomm.propify_api.entity.Project; +import de.iwomm.propify_api.entity.Property; +import de.iwomm.propify_api.repository.ProjectRepository; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Service +public class ProjectService { + + private final ProjectRepository projectRepository; + private final PropertyService propertyService; + + public ProjectService(ProjectRepository projectRepository, PropertyService propertyService) { + this.projectRepository = projectRepository; + this.propertyService = propertyService; + } + + public List findAll() { + return projectRepository.findAll(); + } + + public Optional findById(UUID id) { + return projectRepository.findById(id); + } + + public Project save(ProjectDTO dto) { + + Property property = this.propertyService.findById(dto.getPropertyId()).orElseThrow(EntityNotFoundException::new); + + return this.projectRepository.save(new Project( + dto.getName(), + dto.getEventNumber(), + dto.getDescription(), + dto.getProjectType(), + dto.getStatus(), + property, + dto.getAmountRequested(), + dto.getStartDate(), + dto.getEndDate() + )); + } + + public ProjectDTO toDTO(Project project) { + return new ProjectDTO( + project.getId(), + project.getName(), + project.getEventNumber(), + project.getDescription(), + project.getProjectType(), + project.getStatus(), + project.getProperty().getId(), + project.getAmountRequested(), + project.getStartDate(), + project.getEndDate(), + project.getCreatedAt(), + new PropertyDTO( + project.getProperty().getId(), + project.getProperty().getName(), + project.getProperty().getStreet(), + project.getProperty().getHouseNumber(), + project.getProperty().getZipCode(), + project.getProperty().getCity(), + project.getProperty().getCountry(), + project.getProperty().getNotes(), + new ArrayList<>() + ) + ); + } + + public List toDTOs(List projects) { + List dtos = new ArrayList<>(); + + projects.forEach(project -> { + dtos.add(this.toDTO(project)); + }); + + return dtos; + } + + public ProjectStatsDTO getStatsDTO() { + ProjectStatsDTO projectStatsDTO = new ProjectStatsDTO(); + + projectStatsDTO.setAmountRequestedInTotal(this.projectRepository.sumAmountRequested()); + projectStatsDTO.setProjectsCountTotal(this.projectRepository.count()); + projectStatsDTO.setProjectsCountActive(this.projectRepository.countProjectByStatus("active")); + projectStatsDTO.setProjectsCountCompleted(this.projectRepository.countProjectByStatus("completed")); + + return projectStatsDTO; + } +} diff --git a/src/main/java/de/iwomm/propify_api/service/PropertyService.java b/src/main/java/de/iwomm/propify_api/service/PropertyService.java index a392ba9..82881c3 100644 --- a/src/main/java/de/iwomm/propify_api/service/PropertyService.java +++ b/src/main/java/de/iwomm/propify_api/service/PropertyService.java @@ -3,7 +3,6 @@ package de.iwomm.propify_api.service; import de.iwomm.propify_api.dto.AttachmentDTO; import de.iwomm.propify_api.dto.BulkDeletePropertyIdsDTO; import de.iwomm.propify_api.dto.PropertyDTO; -import de.iwomm.propify_api.entity.Attachment; import de.iwomm.propify_api.entity.Property; import de.iwomm.propify_api.repository.PropertyRepository; import jakarta.persistence.EntityNotFoundException;