diff --git a/backend/src/main/java/com/petshop/backend/service/AdoptionService.java b/backend/src/main/java/com/petshop/backend/service/AdoptionService.java index 0e3c7a95..9855a3e6 100644 --- a/backend/src/main/java/com/petshop/backend/service/AdoptionService.java +++ b/backend/src/main/java/com/petshop/backend/service/AdoptionService.java @@ -81,6 +81,9 @@ public class AdoptionService { : null; String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus()); validatePetAvailability(pet, null, null); + if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) && request.getAdoptionDate() != null && request.getAdoptionDate().isAfter(LocalDate.now())) { + throw new IllegalArgumentException("Cannot mark a future-dated adoption as Completed"); + } Adoption adoption = new Adoption(); adoption.setPet(pet); @@ -91,7 +94,7 @@ public class AdoptionService { adoption.setAdoptionStatus(adoptionStatus); adoption = adoptionRepository.save(adoption); - syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId()); + syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId(), customer); return mapToResponse(adoption); } @@ -113,6 +116,9 @@ public class AdoptionService { String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus()); Long currentPetId = adoption.getPet() != null ? adoption.getPet().getPetId() : null; validatePetAvailability(pet, adoption.getAdoptionId(), currentPetId); + if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) && request.getAdoptionDate() != null && request.getAdoptionDate().isAfter(LocalDate.now())) { + throw new IllegalArgumentException("Cannot mark a future-dated adoption as Completed"); + } adoption.setPet(pet); adoption.setCustomer(customer); @@ -122,7 +128,7 @@ public class AdoptionService { adoption.setAdoptionStatus(adoptionStatus); adoption = adoptionRepository.save(adoption); - syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId()); + syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId(), customer); return mapToResponse(adoption); } @@ -216,13 +222,16 @@ public class AdoptionService { } } - private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId) { + private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId, User customer) { boolean completedElsewhere = adoptionId != null && adoptionRepository.existsByPet_IdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(pet.getPetId(), ADOPTION_STATUS_COMPLETED, adoptionId); if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) || completedElsewhere) { pet.setPetStatus(PET_STATUS_ADOPTED); + pet.setOwner(customer); + pet.setStore(null); } else { pet.setPetStatus(PET_STATUS_AVAILABLE); + pet.setOwner(null); } petRepository.save(pet); } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java index 139255c6..495813ac 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AppointmentDialogController.java @@ -18,6 +18,7 @@ import org.example.petshopdesktop.api.endpoints.DropdownApi; import org.example.petshopdesktop.auth.UserSession; import org.example.petshopdesktop.util.ActivityLogger; +import java.time.DayOfWeek; import java.time.LocalTime; import java.time.LocalDate; import java.util.List; @@ -65,7 +66,15 @@ public class AppointmentDialogController { cbPet.setPromptText("Select a customer first"); cbCustomer.setPromptText("Select a customer"); cbService.setPromptText("Select a service"); - dpAppointmentDate.setValue(LocalDate.now().plusDays(1)); + LocalDate minDate = minAppointmentDate(); + dpAppointmentDate.setValue(minDate); + dpAppointmentDate.setDayCellFactory(picker -> new javafx.scene.control.DateCell() { + @Override + public void updateItem(LocalDate item, boolean empty) { + super.updateItem(item, empty); + setDisable(empty || item.isBefore(minDate)); + } + }); cbAppointmentStatus.setValue("Booked"); for (int i = 9; i <= 17; i++) { @@ -315,9 +324,20 @@ public class AppointmentDialogController { try { List pets = DropdownApi.getInstance().getCustomerPets(customerId); Platform.runLater(() -> { - cbPet.setItems(FXCollections.observableArrayList(pets)); - cbPet.setDisable(pets == null || pets.isEmpty()); - cbPet.setPromptText(pets == null || pets.isEmpty() ? "No pets for selected customer" : "Select a pet"); + ObservableList petOptions = FXCollections.observableArrayList(pets != null ? pets : List.of()); + if (pendingPetSelectionId != null) { + boolean found = petOptions.stream().anyMatch(p -> pendingPetSelectionId.equals(p.getId())); + if (!found && selectedAppointment != null && selectedAppointment.getPetId() > 0) { + DropdownOption placeholder = new DropdownOption(); + placeholder.setId((long) selectedAppointment.getPetId()); + placeholder.setLabel(selectedAppointment.getPetName() != null && !selectedAppointment.getPetName().isBlank() + ? selectedAppointment.getPetName() : "Pet #" + selectedAppointment.getPetId()); + petOptions.add(0, placeholder); + } + } + cbPet.setItems(petOptions); + cbPet.setDisable(petOptions.isEmpty()); + cbPet.setPromptText(petOptions.isEmpty() ? "No pets for selected customer" : "Select a pet"); if (pendingPetSelectionId != null) { for (DropdownOption pet : cbPet.getItems()) { if (pet.getId() != null && pet.getId().equals(pendingPetSelectionId)) { @@ -392,6 +412,19 @@ public class AppointmentDialogController { }).start(); } + private LocalDate minAppointmentDate() { + LocalDate date = LocalDate.now(); + int businessDaysAdded = 0; + while (businessDaysAdded < 2) { + date = date.plusDays(1); + DayOfWeek dow = date.getDayOfWeek(); + if (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY) { + businessDaysAdded++; + } + } + return date; + } + private void loadEmployees() { new Thread(() -> { try { diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java index 5e4835e2..c6794758 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/PetDialogController.java @@ -247,7 +247,7 @@ public class PetDialogController { request.setPetAge(age); String status = cbPetStatus.getValue(); - if ("Owned".equalsIgnoreCase(status) && cbCustomer.getValue() != null) { + if (("Owned".equalsIgnoreCase(status) || "Adopted".equalsIgnoreCase(status)) && cbCustomer.getValue() != null) { request.setCustomerId(cbCustomer.getValue().getId()); } if (requiresStore(status) && cbStore.getValue() != null) { @@ -432,9 +432,9 @@ public class PetDialogController { } private void updateStatusFieldVisibility(String status) { - boolean owned = "Owned".equalsIgnoreCase(status); + boolean needsCustomer = "Owned".equalsIgnoreCase(status) || "Adopted".equalsIgnoreCase(status); boolean storeBased = requiresStore(status); - setFieldVisibility(vbCustomerField, owned); + setFieldVisibility(vbCustomerField, needsCustomer); setFieldVisibility(vbStoreField, storeBased); }