diff --git a/pom.xml b/pom.xml
index 5d37f08..85d43dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,10 @@
spring-boot-docker-compose
runtime
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java
new file mode 100644
index 0000000..3992be2
--- /dev/null
+++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java
@@ -0,0 +1,13 @@
+package org.example.vet1177.dto.request.medicalrecord;
+
+
+
+import jakarta.validation.constraints.NotNull;
+
+import java.util.UUID;
+
+// Tilldela handläggare
+public record AssignVetRequest(
+ @NotNull(message = "Veterinär måste anges")
+ UUID vetId
+) {}
\ No newline at end of file
diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java
new file mode 100644
index 0000000..6db18b9
--- /dev/null
+++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java
@@ -0,0 +1,24 @@
+package org.example.vet1177.dto.request.medicalrecord;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+import java.util.UUID;
+
+// Skapa nytt ärende
+public record CreateMedicalRecordRequest(
+ @NotBlank(message = "Titel får inte vara tom")
+ @Size(max = 500, message = "Titel får max vara 500 tecken")
+ String title,
+
+ @Size(max = 5000, message = "Beskrivning får max vara 5000 tecken")
+ String description,
+
+ @NotNull(message = "Djur måste anges")
+ UUID petId,
+
+ @NotNull(message = "Klinik måste anges")
+ UUID clinicId
+
+) {}
\ No newline at end of file
diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java
new file mode 100644
index 0000000..7437acb
--- /dev/null
+++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java
@@ -0,0 +1,14 @@
+package org.example.vet1177.dto.request.medicalrecord;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+// Uppdatera titel och beskrivning
+public record UpdateMedicalRecordRequest(
+ @NotBlank(message = "Titel får inte vara tom")
+ @Size(max = 500, message = "Titel får max vara 500 tecken")
+ String title,
+
+ @Size(max = 5000, message = "Beskrivning får max vara 5000 tecken")
+ String description
+) {}
\ No newline at end of file
diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java
new file mode 100644
index 0000000..d3a53ad
--- /dev/null
+++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java
@@ -0,0 +1,11 @@
+package org.example.vet1177.dto.request.medicalrecord;
+
+
+import jakarta.validation.constraints.NotNull;
+import org.example.vet1177.entities.RecordStatus;
+
+// Uppdatera status
+public record UpdateStatusRequest(
+ @NotNull(message = "Status måste anges")
+ RecordStatus status
+) {}
\ No newline at end of file
diff --git a/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java
new file mode 100644
index 0000000..a75febb
--- /dev/null
+++ b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java
@@ -0,0 +1,52 @@
+package org.example.vet1177.dto.response.medicalrecord;
+
+import org.example.vet1177.entities.MedicalRecord;
+import org.example.vet1177.entities.RecordStatus;
+
+import java.time.Instant;
+import java.util.UUID;
+
+//fullständigt svar för getByID:
+public record MedicalRecordResponse(
+ UUID id,
+ String title,
+ String description,
+ RecordStatus status,
+ UUID petId,
+ String petName,
+ String petSpecies,
+ UUID ownerId,
+ String ownerName,
+ UUID clinicId,
+ String clinicName,
+ UUID assignedVetId,
+ String assignedVetName,
+ UUID createdById,
+ String createdByName,
+ Instant createdAt,
+ Instant updatedAt,
+ Instant closedAt
+) {
+ public static MedicalRecordResponse from(MedicalRecord record) {
+ return new MedicalRecordResponse(
+ record.getId(),
+ record.getTitle(),
+ record.getDescription(),
+ record.getStatus(),
+ record.getPet().getId(),
+ record.getPet().getName(),
+ record.getPet().getSpecies(),
+ record.getOwner().getId(),
+ record.getOwner().getName(),
+ record.getClinic().getId(),
+ record.getClinic().getName(),
+ record.getAssignedVet() != null ? record.getAssignedVet().getId() : null,
+ record.getAssignedVet() != null ? record.getAssignedVet().getName() : null,
+ record.getCreatedBy().getId(),
+ record.getCreatedBy().getName(),
+ record.getCreatedAt(),
+ record.getUpdatedAt(),
+ record.getClosedAt()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java
new file mode 100644
index 0000000..89daff4
--- /dev/null
+++ b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java
@@ -0,0 +1,30 @@
+package org.example.vet1177.dto.response.medicalrecord;
+
+import org.example.vet1177.entities.MedicalRecord;
+import org.example.vet1177.entities.RecordStatus;
+
+import java.time.Instant;
+import java.util.UUID;
+
+// Förenklat svar som används i listor
+public record MedicalRecordSummaryResponse(
+ UUID id,
+ String title,
+ RecordStatus status,
+ String petName,
+ String ownerName,
+ String assignedVetName,
+ Instant createdAt
+) {
+ public static MedicalRecordSummaryResponse from(MedicalRecord record) {
+ return new MedicalRecordSummaryResponse(
+ record.getId(),
+ record.getTitle(),
+ record.getStatus(),
+ record.getPet().getName(),
+ record.getOwner().getName(),
+ record.getAssignedVet() != null ? record.getAssignedVet().getName() : null,
+ record.getCreatedAt()
+ );
+ }
+}
diff --git a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java
index 621224f..88507aa 100644
--- a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java
+++ b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java
@@ -2,30 +2,34 @@
import org.example.vet1177.entities.MedicalRecord;
import org.example.vet1177.entities.RecordStatus;
+import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
@Repository
public interface MedicalRecordRepository extends JpaRepository {
- // Hämta alla ärenden för ett specifikt djur
+ @EntityGraph(attributePaths = {
+ "pet", "owner", "clinic", "assignedVet", "createdBy"
+ })
+ Optional findById(UUID id);
+
+ @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"})
List findByPetId(UUID petId);
- // Hämta alla ärenden för en ägare
+ @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"})
List findByOwnerId(UUID ownerId);
- // Hämta alla ärenden tilldelade en specifik vet
- List findByAssignedVetId(UUID vetId);
-
- // Hämta alla ärenden för en klinik
+ @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"})
List findByClinicId(UUID clinicId);
- // Hämta ärenden filtrerat på status
- List findByStatus(RecordStatus status);
-
- // Kombinerat — ärenden för en klinik med specifik status (användbart för ABAC)
+ @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"})
List findByClinicIdAndStatus(UUID clinicId, RecordStatus status);
+
+ List findByAssignedVetId(UUID vetId);
+ List findByStatus(RecordStatus status);
}
\ No newline at end of file
diff --git a/src/main/java/org/example/vet1177/services/MedicalRecordService.java b/src/main/java/org/example/vet1177/services/MedicalRecordService.java
index 2924400..65b0aea 100644
--- a/src/main/java/org/example/vet1177/services/MedicalRecordService.java
+++ b/src/main/java/org/example/vet1177/services/MedicalRecordService.java
@@ -1,6 +1,8 @@
package org.example.vet1177.services;
import org.example.vet1177.entities.*;
+import org.example.vet1177.exception.BusinessRuleException;
+import org.example.vet1177.exception.ResourceNotFoundException;
import org.example.vet1177.repository.MedicalRecordRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -46,7 +48,7 @@ public MedicalRecord create(
@Transactional(readOnly = true)
public MedicalRecord getById(UUID id) {
return medicalRecordRepository.findById(id)
- .orElseThrow(() -> new RuntimeException("Ärende hittades inte: " + id));
+ .orElseThrow(() -> new ResourceNotFoundException("MedicalRecord", id)); // ← rad 49
}
@Transactional(readOnly = true)
@@ -73,6 +75,11 @@ public List getByClinicAndStatus(UUID clinicId, RecordStatus stat
public MedicalRecord update(UUID id, String title, String description, User updatedBy) {
MedicalRecord record = getById(id);
+
+ if (record.getStatus().isFinal()) { // ← mellan rad 75-76
+ throw new BusinessRuleException("Stängda ärenden kan inte uppdateras");
+ }
+
record.setTitle(title);
record.setDescription(description);
record.setUpdatedBy(updatedBy);
@@ -104,8 +111,8 @@ public MedicalRecord updateStatus(UUID recordId, RecordStatus newStatus, User up
public MedicalRecord close(UUID recordId, User closedBy) {
MedicalRecord record = getById(recordId);
- if (record.getStatus() == RecordStatus.CLOSED) {
- throw new RuntimeException("Ärendet är redan stängt");
+ if (record.getStatus().isFinal()) { // ← rad 108
+ throw new BusinessRuleException("Ärendet är redan stängt");
}
record.setStatus(RecordStatus.CLOSED);