Implemented project handling and updated property components according to front-end needs

This commit is contained in:
2025-09-12 23:41:58 +02:00
parent 5eb6b6e738
commit 8b6083c1c0
9 changed files with 576 additions and 7 deletions

View File

@@ -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<ProjectDTO> 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<ProjectDTO> 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();
}
}
}

View File

@@ -35,7 +35,7 @@ public class PropertyController {
@GetMapping @GetMapping
@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER')")
public ResponseEntity<?> getAllProperties() { public ResponseEntity<?> getAll() {
List<PropertyDTO> propertiesDTO = propertyService.toDTOs(propertyService.findAll()); List<PropertyDTO> propertiesDTO = propertyService.toDTOs(propertyService.findAll());
return ResponseEntity return ResponseEntity
@@ -44,7 +44,7 @@ public class PropertyController {
@GetMapping("/{id}") @GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN', 'USER')") @PreAuthorize("hasAnyRole('ADMIN', 'USER')")
public ResponseEntity<?> getPropertyById(@PathVariable UUID id) { public ResponseEntity<?> getById(@PathVariable UUID id) {
try { try {
return ResponseEntity return ResponseEntity
.ok(propertyService.findById(id).orElseThrow(EntityNotFoundException::new)); .ok(propertyService.findById(id).orElseThrow(EntityNotFoundException::new));
@@ -107,7 +107,7 @@ public class PropertyController {
@PostMapping @PostMapping
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> createProperty(@RequestBody PropertyDTO propertyDTO) { public ResponseEntity<?> create(@RequestBody PropertyDTO propertyDTO) {
try { try {
Property newItem = propertyService.saveDTO(propertyDTO); Property newItem = propertyService.saveDTO(propertyDTO);
@@ -128,7 +128,7 @@ public class PropertyController {
@PatchMapping("/{id}") @PatchMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> updateProperty(@PathVariable UUID id, @RequestBody PropertyDTO propertyDTO) { public ResponseEntity<?> update(@PathVariable UUID id, @RequestBody PropertyDTO propertyDTO) {
try { try {
return ResponseEntity return ResponseEntity
.ok(propertyService.update(id, propertyDTO)); .ok(propertyService.update(id, propertyDTO));
@@ -145,7 +145,7 @@ public class PropertyController {
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> deleteProperty(@PathVariable UUID id) { public ResponseEntity<?> delete(@PathVariable UUID id) {
try { try {
propertyService.deleteById(id); propertyService.deleteById(id);
@@ -165,7 +165,7 @@ public class PropertyController {
@PostMapping("/bulk-delete") @PostMapping("/bulk-delete")
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> deleteProperties(@RequestBody BulkDeletePropertyIdsDTO propertiesDTO) { public ResponseEntity<?> deleteMany(@RequestBody BulkDeletePropertyIdsDTO propertiesDTO) {
try { try {
propertyService.deleteByIds(propertiesDTO); propertyService.deleteByIds(propertiesDTO);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,8 +1,10 @@
package de.iwomm.propify_api.entity; package de.iwomm.propify_api.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -41,6 +43,19 @@ public class Property {
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@OneToMany(mappedBy = "property", cascade = CascadeType.PERSIST, orphanRemoval = true)
@OrderBy("createdAt")
@JsonBackReference
private List<Project> projects = new ArrayList<>();
public List<Project> getProjects() {
return projects;
}
public void setProjects(List<Project> projects) {
this.projects = projects;
}
public List<Attachment> getAttachments() { public List<Attachment> getAttachments() {
return attachments; return attachments;
} }

View File

@@ -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<Project, UUID> {
public Optional<Project> findByName(String name);
@Query("SELECT SUM(p.amountRequested) FROM Project p")
long sumAmountRequested();
long countProjectByStatus(String status);
}

View File

@@ -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<Project> findAll() {
return projectRepository.findAll();
}
public Optional<Project> 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<ProjectDTO> toDTOs(List<Project> projects) {
List<ProjectDTO> 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;
}
}

View File

@@ -3,7 +3,6 @@ package de.iwomm.propify_api.service;
import de.iwomm.propify_api.dto.AttachmentDTO; import de.iwomm.propify_api.dto.AttachmentDTO;
import de.iwomm.propify_api.dto.BulkDeletePropertyIdsDTO; import de.iwomm.propify_api.dto.BulkDeletePropertyIdsDTO;
import de.iwomm.propify_api.dto.PropertyDTO; 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.entity.Property;
import de.iwomm.propify_api.repository.PropertyRepository; import de.iwomm.propify_api.repository.PropertyRepository;
import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.EntityNotFoundException;