Harden assignment rules
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
|
import com.petshop.backend.repository.CustomerPetRepository;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Profile("local")
|
||||||
|
public class LocalAppointmentCustomerSeedInitializer implements CommandLineRunner {
|
||||||
|
|
||||||
|
private final DataSource dataSource;
|
||||||
|
private final CustomerPetRepository customerPetRepository;
|
||||||
|
|
||||||
|
public LocalAppointmentCustomerSeedInitializer(DataSource dataSource, CustomerPetRepository customerPetRepository) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
this.customerPetRepository = customerPetRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
if (customerPetRepository.count() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(false, false, "UTF-8",
|
||||||
|
new ClassPathResource("dev/seed_demo_customer_pets.sql"));
|
||||||
|
populator.execute(dataSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,21 +71,8 @@ public class AdoptionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<AdoptionResponse> createAdoption(@Valid @RequestBody AdoptionRequest request) {
|
public ResponseEntity<AdoptionResponse> createAdoption(@Valid @RequestBody AdoptionRequest request) {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
||||||
String role = authentication.getAuthorities().stream()
|
|
||||||
.findFirst()
|
|
||||||
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
if (role != null && role.equals("CUSTOMER")) {
|
|
||||||
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
|
|
||||||
if (!request.getCustomerId().equals(customer.getCustomerId())) {
|
|
||||||
throw new org.springframework.security.access.AccessDeniedException("You can only create adoptions for yourself");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(adoptionService.createAdoption(request));
|
return ResponseEntity.status(HttpStatus.CREATED).body(adoptionService.createAdoption(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,16 @@ public class DropdownController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/adoption-pets")
|
||||||
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
|
public ResponseEntity<List<DropdownOption>> getAdoptionPets() {
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
petRepository.findAllByPetStatusIgnoreCaseOrderByPetNameAsc("Available").stream()
|
||||||
|
.map(p -> new DropdownOption(p.getPetId(), p.getPetName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/customers")
|
@GetMapping("/customers")
|
||||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<List<DropdownOption>> getCustomers() {
|
public ResponseEntity<List<DropdownOption>> getCustomers() {
|
||||||
@@ -67,6 +77,16 @@ public class DropdownController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/appointment-customers")
|
||||||
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
|
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
customerRepository.findAllWithPets().stream()
|
||||||
|
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/customers/{customerId}/pets")
|
@GetMapping("/customers/{customerId}/pets")
|
||||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<List<DropdownOption>> getCustomerPets(@PathVariable Long customerId) {
|
public ResponseEntity<List<DropdownOption>> getCustomerPets(@PathVariable Long customerId) {
|
||||||
@@ -174,8 +194,8 @@ public class DropdownController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return userRepository.findById(userId)
|
return userRepository.findById(userId)
|
||||||
.map(User::getRole)
|
.filter(user -> user.getRole() == User.Role.STAFF)
|
||||||
.filter(role -> role == User.Role.STAFF)
|
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,8 @@ public interface AdoptionRepository extends JpaRepository<Adoption, Long> {
|
|||||||
Page<Adoption> searchAdoptionsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable);
|
Page<Adoption> searchAdoptionsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable);
|
||||||
|
|
||||||
Optional<Adoption> findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(Long petId, String adoptionStatus);
|
Optional<Adoption> findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(Long petId, String adoptionStatus);
|
||||||
|
|
||||||
|
boolean existsByPetPetIdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(Long petId, String adoptionStatus, Long adoptionId);
|
||||||
|
|
||||||
|
boolean existsByPetPetIdAndAdoptionStatusIgnoreCase(Long petId, String adoptionStatus);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public interface CustomerRepository extends JpaRepository<Customer, Long> {
|
|||||||
|
|
||||||
Optional<Customer> findByUserId(Long userId);
|
Optional<Customer> findByUserId(Long userId);
|
||||||
List<Customer> findAllByEmail(String email);
|
List<Customer> findAllByEmail(String email);
|
||||||
|
|
||||||
|
@Query("SELECT DISTINCT c FROM Customer c WHERE EXISTS (SELECT cp FROM CustomerPet cp WHERE cp.customer = c) ORDER BY c.firstName ASC, c.lastName ASC")
|
||||||
|
List<Customer> findAllWithPets();
|
||||||
|
|
||||||
@Query("SELECT c FROM Customer c WHERE " +
|
@Query("SELECT c FROM Customer c WHERE " +
|
||||||
"LOWER(c.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
"LOWER(c.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ import org.springframework.data.jpa.repository.Query;
|
|||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface PetRepository extends JpaRepository<Pet, Long> {
|
public interface PetRepository extends JpaRepository<Pet, Long> {
|
||||||
|
|
||||||
|
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
|
||||||
|
|
||||||
@Query("SELECT p FROM Pet p WHERE " +
|
@Query("SELECT p FROM Pet p WHERE " +
|
||||||
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||||
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
|
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@Service
|
@Service
|
||||||
public class AdoptionService {
|
public class AdoptionService {
|
||||||
|
|
||||||
|
private static final String ADOPTION_STATUS_PENDING = "Pending";
|
||||||
|
private static final String ADOPTION_STATUS_COMPLETED = "Completed";
|
||||||
|
private static final String ADOPTION_STATUS_CANCELLED = "Cancelled";
|
||||||
|
private static final String PET_STATUS_AVAILABLE = "Available";
|
||||||
|
private static final String PET_STATUS_ADOPTED = "Adopted";
|
||||||
|
|
||||||
private final AdoptionRepository adoptionRepository;
|
private final AdoptionRepository adoptionRepository;
|
||||||
private final PetRepository petRepository;
|
private final PetRepository petRepository;
|
||||||
private final CustomerRepository customerRepository;
|
private final CustomerRepository customerRepository;
|
||||||
@@ -75,15 +81,18 @@ public class AdoptionService {
|
|||||||
Customer customer = customerRepository.findById(request.getCustomerId())
|
Customer customer = customerRepository.findById(request.getCustomerId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
||||||
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
|
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
|
||||||
|
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
|
||||||
|
validatePetAvailability(pet, null);
|
||||||
|
|
||||||
Adoption adoption = new Adoption();
|
Adoption adoption = new Adoption();
|
||||||
adoption.setPet(pet);
|
adoption.setPet(pet);
|
||||||
adoption.setCustomer(customer);
|
adoption.setCustomer(customer);
|
||||||
adoption.setEmployee(employee);
|
adoption.setEmployee(employee);
|
||||||
adoption.setAdoptionDate(request.getAdoptionDate());
|
adoption.setAdoptionDate(request.getAdoptionDate());
|
||||||
adoption.setAdoptionStatus(request.getAdoptionStatus());
|
adoption.setAdoptionStatus(adoptionStatus);
|
||||||
|
|
||||||
adoption = adoptionRepository.save(adoption);
|
adoption = adoptionRepository.save(adoption);
|
||||||
|
syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId());
|
||||||
return mapToResponse(adoption);
|
return mapToResponse(adoption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,14 +107,17 @@ public class AdoptionService {
|
|||||||
Customer customer = customerRepository.findById(request.getCustomerId())
|
Customer customer = customerRepository.findById(request.getCustomerId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
||||||
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
|
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
|
||||||
|
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
|
||||||
|
validatePetAvailability(pet, adoption.getAdoptionId());
|
||||||
|
|
||||||
adoption.setPet(pet);
|
adoption.setPet(pet);
|
||||||
adoption.setCustomer(customer);
|
adoption.setCustomer(customer);
|
||||||
adoption.setEmployee(employee);
|
adoption.setEmployee(employee);
|
||||||
adoption.setAdoptionDate(request.getAdoptionDate());
|
adoption.setAdoptionDate(request.getAdoptionDate());
|
||||||
adoption.setAdoptionStatus(request.getAdoptionStatus());
|
adoption.setAdoptionStatus(adoptionStatus);
|
||||||
|
|
||||||
adoption = adoptionRepository.save(adoption);
|
adoption = adoptionRepository.save(adoption);
|
||||||
|
syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId());
|
||||||
return mapToResponse(adoption);
|
return mapToResponse(adoption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,8 +173,49 @@ public class AdoptionService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return userRepository.findById(userId)
|
return userRepository.findById(userId)
|
||||||
.map(User::getRole)
|
.filter(user -> user.getRole() == User.Role.STAFF)
|
||||||
.filter(role -> role == User.Role.STAFF)
|
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeAdoptionStatus(String adoptionStatus) {
|
||||||
|
if (adoptionStatus == null) {
|
||||||
|
throw new IllegalArgumentException("Adoption status is required");
|
||||||
|
}
|
||||||
|
String trimmedStatus = adoptionStatus.trim();
|
||||||
|
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(trimmedStatus)) {
|
||||||
|
return ADOPTION_STATUS_PENDING;
|
||||||
|
}
|
||||||
|
if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(trimmedStatus)) {
|
||||||
|
return ADOPTION_STATUS_COMPLETED;
|
||||||
|
}
|
||||||
|
if (ADOPTION_STATUS_CANCELLED.equalsIgnoreCase(trimmedStatus)) {
|
||||||
|
return ADOPTION_STATUS_CANCELLED;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Adoption status must be Pending, Completed, or Cancelled");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validatePetAvailability(Pet pet, Long adoptionId) {
|
||||||
|
boolean adoptedElsewhere = adoptionId == null
|
||||||
|
? adoptionRepository.existsByPetPetIdAndAdoptionStatusIgnoreCase(pet.getPetId(), ADOPTION_STATUS_COMPLETED)
|
||||||
|
: adoptionRepository.existsByPetPetIdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(pet.getPetId(), ADOPTION_STATUS_COMPLETED, adoptionId);
|
||||||
|
if (adoptedElsewhere) {
|
||||||
|
throw new IllegalArgumentException("Selected pet has already been adopted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PET_STATUS_AVAILABLE.equalsIgnoreCase(pet.getPetStatus()) && adoptionId == null) {
|
||||||
|
throw new IllegalArgumentException("Selected pet is not available for adoption");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId) {
|
||||||
|
boolean completedElsewhere = adoptionId != null
|
||||||
|
&& adoptionRepository.existsByPetPetIdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(pet.getPetId(), ADOPTION_STATUS_COMPLETED, adoptionId);
|
||||||
|
if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) || completedElsewhere) {
|
||||||
|
pet.setPetStatus(PET_STATUS_ADOPTED);
|
||||||
|
} else {
|
||||||
|
pet.setPetStatus(PET_STATUS_AVAILABLE);
|
||||||
|
}
|
||||||
|
petRepository.save(pet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,8 +333,8 @@ public class AppointmentService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return userRepository.findById(userId)
|
return userRepository.findById(userId)
|
||||||
.map(User::getRole)
|
.filter(user -> user.getRole() == User.Role.STAFF)
|
||||||
.filter(role -> role == User.Role.STAFF)
|
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
53
backend/src/main/resources/dev/seed_demo_customer_pets.sql
Normal file
53
backend/src/main/resources/dev/seed_demo_customer_pets.sql
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
INSERT INTO customer_pet (customer_id, pet_name, species, breed, image_url)
|
||||||
|
SELECT c.customerId, 'Rocky', 'dog', 'Labrador', NULL
|
||||||
|
FROM customer c
|
||||||
|
WHERE c.email = 'alex@gmail.com'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM customer_pet cp
|
||||||
|
WHERE cp.customer_id = c.customerId AND cp.pet_name = 'Rocky'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO customer_pet (customer_id, pet_name, species, breed, image_url)
|
||||||
|
SELECT c.customerId, 'Whiskers', 'cat', 'Persian', NULL
|
||||||
|
FROM customer c
|
||||||
|
WHERE c.email = 'emily@gmail.com'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM customer_pet cp
|
||||||
|
WHERE cp.customer_id = c.customerId AND cp.pet_name = 'Whiskers'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO customer_pet (customer_id, pet_name, species, breed, image_url)
|
||||||
|
SELECT c.customerId, 'Daisy', 'dog', 'Beagle', NULL
|
||||||
|
FROM customer c
|
||||||
|
WHERE c.email = 'james@gmail.com'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM customer_pet cp
|
||||||
|
WHERE cp.customer_id = c.customerId AND cp.pet_name = 'Daisy'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO customer_pet (customer_id, pet_name, species, breed, image_url)
|
||||||
|
SELECT c.customerId, 'Pepper', 'cat', 'Domestic Shorthair', NULL
|
||||||
|
FROM customer c
|
||||||
|
WHERE c.email = 'olivia@gmail.com'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM customer_pet cp
|
||||||
|
WHERE cp.customer_id = c.customerId AND cp.pet_name = 'Pepper'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO customer_pet (customer_id, pet_name, species, breed, image_url)
|
||||||
|
SELECT c.customerId, 'Cooper', 'dog', 'Golden Retriever', NULL
|
||||||
|
FROM customer c
|
||||||
|
WHERE c.email = 'william@gmail.com'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM customer_pet cp
|
||||||
|
WHERE cp.customer_id = c.customerId AND cp.pet_name = 'Cooper'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO customer_pet (customer_id, pet_name, species, breed, image_url)
|
||||||
|
SELECT c.customerId, 'Mittens', 'cat', 'Siamese', NULL
|
||||||
|
FROM customer c
|
||||||
|
WHERE c.email = 'sophia@gmail.com'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM customer_pet cp
|
||||||
|
WHERE cp.customer_id = c.customerId AND cp.pet_name = 'Mittens'
|
||||||
|
);
|
||||||
@@ -2,6 +2,7 @@ package com.petshop.backend.controller;
|
|||||||
|
|
||||||
import com.petshop.backend.entity.Employee;
|
import com.petshop.backend.entity.Employee;
|
||||||
import com.petshop.backend.entity.EmployeeStore;
|
import com.petshop.backend.entity.EmployeeStore;
|
||||||
|
import com.petshop.backend.entity.Customer;
|
||||||
import com.petshop.backend.entity.StoreLocation;
|
import com.petshop.backend.entity.StoreLocation;
|
||||||
import com.petshop.backend.entity.User;
|
import com.petshop.backend.entity.User;
|
||||||
import com.petshop.backend.repository.CategoryRepository;
|
import com.petshop.backend.repository.CategoryRepository;
|
||||||
@@ -71,10 +72,12 @@ class DropdownControllerTest {
|
|||||||
User staffUser = new User();
|
User staffUser = new User();
|
||||||
staffUser.setId(7L);
|
staffUser.setId(7L);
|
||||||
staffUser.setRole(User.Role.STAFF);
|
staffUser.setRole(User.Role.STAFF);
|
||||||
|
staffUser.setActive(true);
|
||||||
|
|
||||||
User adminUser = new User();
|
User adminUser = new User();
|
||||||
adminUser.setId(8L);
|
adminUser.setId(8L);
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
adminUser.setActive(true);
|
||||||
|
|
||||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||||
.thenReturn(List.of(new EmployeeStore(staffEmployee, store), new EmployeeStore(adminEmployee, store)));
|
.thenReturn(List.of(new EmployeeStore(staffEmployee, store), new EmployeeStore(adminEmployee, store)));
|
||||||
@@ -87,4 +90,99 @@ class DropdownControllerTest {
|
|||||||
assertEquals(Long.valueOf(7L), response.getBody().get(0).getId());
|
assertEquals(Long.valueOf(7L), response.getBody().get(0).getId());
|
||||||
assertEquals("Alex Jones", response.getBody().get(0).getLabel());
|
assertEquals("Alex Jones", response.getBody().get(0).getLabel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getStoreEmployeesExcludesInactiveStaffUsers() {
|
||||||
|
PetRepository petRepository = mock(PetRepository.class);
|
||||||
|
CustomerRepository customerRepository = mock(CustomerRepository.class);
|
||||||
|
CustomerPetRepository customerPetRepository = mock(CustomerPetRepository.class);
|
||||||
|
ServiceRepository serviceRepository = mock(ServiceRepository.class);
|
||||||
|
ProductRepository productRepository = mock(ProductRepository.class);
|
||||||
|
CategoryRepository categoryRepository = mock(CategoryRepository.class);
|
||||||
|
StoreRepository storeRepository = mock(StoreRepository.class);
|
||||||
|
SupplierRepository supplierRepository = mock(SupplierRepository.class);
|
||||||
|
EmployeeStoreRepository employeeStoreRepository = mock(EmployeeStoreRepository.class);
|
||||||
|
UserRepository userRepository = mock(UserRepository.class);
|
||||||
|
|
||||||
|
DropdownController controller = new DropdownController(
|
||||||
|
petRepository,
|
||||||
|
customerRepository,
|
||||||
|
customerPetRepository,
|
||||||
|
serviceRepository,
|
||||||
|
productRepository,
|
||||||
|
categoryRepository,
|
||||||
|
storeRepository,
|
||||||
|
supplierRepository,
|
||||||
|
employeeStoreRepository,
|
||||||
|
userRepository
|
||||||
|
);
|
||||||
|
|
||||||
|
StoreLocation store = new StoreLocation();
|
||||||
|
store.setStoreId(1L);
|
||||||
|
|
||||||
|
Employee inactiveStaffEmployee = new Employee();
|
||||||
|
inactiveStaffEmployee.setEmployeeId(7L);
|
||||||
|
inactiveStaffEmployee.setUserId(7L);
|
||||||
|
inactiveStaffEmployee.setFirstName("Alex");
|
||||||
|
inactiveStaffEmployee.setLastName("Jones");
|
||||||
|
inactiveStaffEmployee.setIsActive(true);
|
||||||
|
|
||||||
|
User inactiveStaffUser = new User();
|
||||||
|
inactiveStaffUser.setId(7L);
|
||||||
|
inactiveStaffUser.setRole(User.Role.STAFF);
|
||||||
|
inactiveStaffUser.setActive(false);
|
||||||
|
|
||||||
|
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||||
|
.thenReturn(List.of(new EmployeeStore(inactiveStaffEmployee, store)));
|
||||||
|
when(userRepository.findById(7L)).thenReturn(Optional.of(inactiveStaffUser));
|
||||||
|
|
||||||
|
var response = controller.getStoreEmployees(1L);
|
||||||
|
|
||||||
|
assertEquals(0, response.getBody().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAppointmentCustomersReturnsOnlyCustomersWithPets() {
|
||||||
|
PetRepository petRepository = mock(PetRepository.class);
|
||||||
|
CustomerRepository customerRepository = mock(CustomerRepository.class);
|
||||||
|
CustomerPetRepository customerPetRepository = mock(CustomerPetRepository.class);
|
||||||
|
ServiceRepository serviceRepository = mock(ServiceRepository.class);
|
||||||
|
ProductRepository productRepository = mock(ProductRepository.class);
|
||||||
|
CategoryRepository categoryRepository = mock(CategoryRepository.class);
|
||||||
|
StoreRepository storeRepository = mock(StoreRepository.class);
|
||||||
|
SupplierRepository supplierRepository = mock(SupplierRepository.class);
|
||||||
|
EmployeeStoreRepository employeeStoreRepository = mock(EmployeeStoreRepository.class);
|
||||||
|
UserRepository userRepository = mock(UserRepository.class);
|
||||||
|
|
||||||
|
DropdownController controller = new DropdownController(
|
||||||
|
petRepository,
|
||||||
|
customerRepository,
|
||||||
|
customerPetRepository,
|
||||||
|
serviceRepository,
|
||||||
|
productRepository,
|
||||||
|
categoryRepository,
|
||||||
|
storeRepository,
|
||||||
|
supplierRepository,
|
||||||
|
employeeStoreRepository,
|
||||||
|
userRepository
|
||||||
|
);
|
||||||
|
|
||||||
|
Customer one = new Customer();
|
||||||
|
one.setCustomerId(1L);
|
||||||
|
one.setFirstName("Alex");
|
||||||
|
one.setLastName("Brown");
|
||||||
|
|
||||||
|
Customer two = new Customer();
|
||||||
|
two.setCustomerId(2L);
|
||||||
|
two.setFirstName("Emily");
|
||||||
|
two.setLastName("Clark");
|
||||||
|
|
||||||
|
when(customerRepository.findAllWithPets()).thenReturn(List.of(one, two));
|
||||||
|
|
||||||
|
var response = controller.getAppointmentCustomers();
|
||||||
|
|
||||||
|
assertEquals(2, response.getBody().size());
|
||||||
|
assertEquals(Long.valueOf(1L), response.getBody().get(0).getId());
|
||||||
|
assertEquals("Alex Brown", response.getBody().get(0).getLabel());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class AdoptionServiceTest {
|
|||||||
pet = new Pet();
|
pet = new Pet();
|
||||||
pet.setPetId(1L);
|
pet.setPetId(1L);
|
||||||
pet.setPetName("Buddy");
|
pet.setPetName("Buddy");
|
||||||
|
pet.setPetStatus("Available");
|
||||||
|
|
||||||
customer = new Customer();
|
customer = new Customer();
|
||||||
customer.setCustomerId(1L);
|
customer.setCustomerId(1L);
|
||||||
@@ -75,11 +76,13 @@ class AdoptionServiceTest {
|
|||||||
User staffUser = new User();
|
User staffUser = new User();
|
||||||
staffUser.setId(7L);
|
staffUser.setId(7L);
|
||||||
staffUser.setRole(User.Role.STAFF);
|
staffUser.setRole(User.Role.STAFF);
|
||||||
|
staffUser.setActive(true);
|
||||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||||
|
|
||||||
User adminUser = new User();
|
User adminUser = new User();
|
||||||
adminUser.setId(8L);
|
adminUser.setId(8L);
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
adminUser.setActive(true);
|
||||||
when(userRepository.findById(8L)).thenReturn(Optional.of(adminUser));
|
when(userRepository.findById(8L)).thenReturn(Optional.of(adminUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,4 +124,26 @@ class AdoptionServiceTest {
|
|||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
|
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createAdoptionRejectsInactiveStaffUserSelection() {
|
||||||
|
User inactiveStaffUser = new User();
|
||||||
|
inactiveStaffUser.setId(7L);
|
||||||
|
inactiveStaffUser.setRole(User.Role.STAFF);
|
||||||
|
inactiveStaffUser.setActive(false);
|
||||||
|
when(userRepository.findById(7L)).thenReturn(Optional.of(inactiveStaffUser));
|
||||||
|
|
||||||
|
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
||||||
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
|
when(employeeRepository.findById(7L)).thenReturn(Optional.of(staffEmployee));
|
||||||
|
|
||||||
|
AdoptionRequest request = new AdoptionRequest();
|
||||||
|
request.setPetId(1L);
|
||||||
|
request.setCustomerId(1L);
|
||||||
|
request.setEmployeeId(7L);
|
||||||
|
request.setAdoptionDate(LocalDate.now());
|
||||||
|
request.setAdoptionStatus("Pending");
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ class AppointmentServiceTest {
|
|||||||
User staffUser = new User();
|
User staffUser = new User();
|
||||||
staffUser.setId(7L);
|
staffUser.setId(7L);
|
||||||
staffUser.setRole(User.Role.STAFF);
|
staffUser.setRole(User.Role.STAFF);
|
||||||
|
staffUser.setActive(true);
|
||||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||||
|
|
||||||
date = LocalDate.now().plusDays(1);
|
date = LocalDate.now().plusDays(1);
|
||||||
@@ -215,6 +216,7 @@ class AppointmentServiceTest {
|
|||||||
adminUser.setId(99L);
|
adminUser.setId(99L);
|
||||||
adminUser.setUsername("admin");
|
adminUser.setUsername("admin");
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
adminUser.setActive(true);
|
||||||
adminUser.setTokenVersion(0);
|
adminUser.setTokenVersion(0);
|
||||||
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
||||||
setAuthentication(99L, User.Role.ADMIN);
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
@@ -253,6 +255,7 @@ class AppointmentServiceTest {
|
|||||||
adminUser.setId(99L);
|
adminUser.setId(99L);
|
||||||
adminUser.setUsername("admin");
|
adminUser.setUsername("admin");
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
adminUser.setActive(true);
|
||||||
adminUser.setTokenVersion(0);
|
adminUser.setTokenVersion(0);
|
||||||
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
||||||
setAuthentication(99L, User.Role.ADMIN);
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
@@ -306,6 +309,7 @@ class AppointmentServiceTest {
|
|||||||
User adminLinkedUser = new User();
|
User adminLinkedUser = new User();
|
||||||
adminLinkedUser.setId(8L);
|
adminLinkedUser.setId(8L);
|
||||||
adminLinkedUser.setRole(User.Role.ADMIN);
|
adminLinkedUser.setRole(User.Role.ADMIN);
|
||||||
|
adminLinkedUser.setActive(true);
|
||||||
|
|
||||||
when(userRepository.findById(8L)).thenReturn(Optional.of(adminLinkedUser));
|
when(userRepository.findById(8L)).thenReturn(Optional.of(adminLinkedUser));
|
||||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
@@ -330,6 +334,45 @@ class AppointmentServiceTest {
|
|||||||
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createAppointmentRejectsInactiveStaffUserSelection() {
|
||||||
|
User adminUser = new User();
|
||||||
|
adminUser.setId(99L);
|
||||||
|
adminUser.setUsername("admin");
|
||||||
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
adminUser.setActive(true);
|
||||||
|
adminUser.setTokenVersion(0);
|
||||||
|
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
||||||
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
|
|
||||||
|
User inactiveStaffUser = new User();
|
||||||
|
inactiveStaffUser.setId(7L);
|
||||||
|
inactiveStaffUser.setRole(User.Role.STAFF);
|
||||||
|
inactiveStaffUser.setActive(false);
|
||||||
|
when(userRepository.findById(7L)).thenReturn(Optional.of(inactiveStaffUser));
|
||||||
|
|
||||||
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
|
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
||||||
|
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
|
||||||
|
when(employeeRepository.findById(7L)).thenReturn(Optional.of(employee));
|
||||||
|
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of());
|
||||||
|
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
|
||||||
|
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||||
|
.thenReturn(List.of(new EmployeeStore(employee, store)));
|
||||||
|
|
||||||
|
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
||||||
|
request.setCustomerId(1L);
|
||||||
|
request.setStoreId(1L);
|
||||||
|
request.setServiceId(1L);
|
||||||
|
request.setEmployeeId(7L);
|
||||||
|
request.setAppointmentDate(date);
|
||||||
|
request.setAppointmentTime(LocalTime.of(10, 0));
|
||||||
|
request.setAppointmentStatus("Booked");
|
||||||
|
request.setCustomerPetIds(List.of(11L));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
||||||
|
}
|
||||||
|
|
||||||
private Appointment appointment(Long id, LocalDate date, LocalTime time, Service service, StoreLocation storeLocation) {
|
private Appointment appointment(Long id, LocalDate date, LocalTime time, Service service, StoreLocation storeLocation) {
|
||||||
Appointment appointment = new Appointment();
|
Appointment appointment = new Appointment();
|
||||||
appointment.setAppointmentId(id);
|
appointment.setAppointmentId(id);
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ public class DropdownApi {
|
|||||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DropdownOption> getAppointmentCustomers() throws Exception {
|
||||||
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/appointment-customers");
|
||||||
|
if (response == null || response.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Empty response from appointment customers endpoint");
|
||||||
|
}
|
||||||
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
|
}
|
||||||
|
|
||||||
public List<DropdownOption> getPets() throws Exception {
|
public List<DropdownOption> getPets() throws Exception {
|
||||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/pets");
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/pets");
|
||||||
if (response == null || response.isEmpty()) {
|
if (response == null || response.isEmpty()) {
|
||||||
@@ -82,6 +90,14 @@ public class DropdownApi {
|
|||||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DropdownOption> getAdoptionPets() throws Exception {
|
||||||
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/adoption-pets");
|
||||||
|
if (response == null || response.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Empty response from adoption pets endpoint");
|
||||||
|
}
|
||||||
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
|
}
|
||||||
|
|
||||||
public List<DropdownOption> getCustomerPets(Long customerId) throws Exception {
|
public List<DropdownOption> getCustomerPets(Long customerId) throws Exception {
|
||||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/customers/" + customerId + "/pets");
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/customers/" + customerId + "/pets");
|
||||||
if (response == null || response.isEmpty()) {
|
if (response == null || response.isEmpty()) {
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ import org.example.petshopdesktop.api.dto.adoption.AdoptionRequest;
|
|||||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
|
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
|
import org.example.petshopdesktop.auth.UserSession;
|
||||||
import org.example.petshopdesktop.models.Adoption;
|
import org.example.petshopdesktop.models.Adoption;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class AdoptionDialogController {
|
public class AdoptionDialogController {
|
||||||
|
|
||||||
@@ -56,6 +58,7 @@ public class AdoptionDialogController {
|
|||||||
|
|
||||||
//Stores if the dialog view is in add/edit mode
|
//Stores if the dialog view is in add/edit mode
|
||||||
private String mode = null;
|
private String mode = null;
|
||||||
|
private Adoption selectedAdoption = null;
|
||||||
|
|
||||||
//Adoption statuses
|
//Adoption statuses
|
||||||
private ObservableList<String> statusList = FXCollections.observableArrayList(
|
private ObservableList<String> statusList = FXCollections.observableArrayList(
|
||||||
@@ -70,11 +73,12 @@ public class AdoptionDialogController {
|
|||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
List<DropdownOption> pets = DropdownApi.getInstance().getPets();
|
List<DropdownOption> pets = DropdownApi.getInstance().getAdoptionPets();
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
if (pets != null) {
|
if (pets != null) {
|
||||||
ObservableList<DropdownOption> petsObs = FXCollections.observableArrayList(pets);
|
ObservableList<DropdownOption> petsObs = FXCollections.observableArrayList(pets);
|
||||||
cbPet.setItems(petsObs);
|
cbPet.setItems(petsObs);
|
||||||
|
applySelectedPet();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -90,9 +94,12 @@ public class AdoptionDialogController {
|
|||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = org.example.petshopdesktop.auth.UserSession.getInstance().getStoreId();
|
Long storeId = UserSession.getInstance().getStoreId();
|
||||||
List<DropdownOption> employees = storeId != null && storeId > 0 ? DropdownApi.getInstance().getStoreEmployees(storeId) : List.of();
|
List<DropdownOption> employees = storeId != null && storeId > 0 ? DropdownApi.getInstance().getStoreEmployees(storeId) : List.of();
|
||||||
Platform.runLater(() -> cbEmployee.setItems(FXCollections.observableArrayList(employees)));
|
Platform.runLater(() -> {
|
||||||
|
cbEmployee.setItems(FXCollections.observableArrayList(employees));
|
||||||
|
applySelectedEmployee();
|
||||||
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
ActivityLogger.getInstance().logException(
|
ActivityLogger.getInstance().logException(
|
||||||
@@ -127,6 +134,7 @@ public class AdoptionDialogController {
|
|||||||
if (customers != null) {
|
if (customers != null) {
|
||||||
ObservableList<DropdownOption> customersObs = FXCollections.observableArrayList(customers);
|
ObservableList<DropdownOption> customersObs = FXCollections.observableArrayList(customers);
|
||||||
cbCustomer.setItems(customersObs);
|
cbCustomer.setItems(customersObs);
|
||||||
|
applySelectedCustomer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -230,30 +238,11 @@ public class AdoptionDialogController {
|
|||||||
|
|
||||||
public void displayAdoptionDetails(Adoption adoption) {
|
public void displayAdoptionDetails(Adoption adoption) {
|
||||||
if (adoption != null) {
|
if (adoption != null) {
|
||||||
|
selectedAdoption = adoption;
|
||||||
lblAdoptionId.setText("ID: " + adoption.getAdoptionId());
|
lblAdoptionId.setText("ID: " + adoption.getAdoptionId());
|
||||||
|
applySelectedPet();
|
||||||
for (DropdownOption pet : cbPet.getItems()) {
|
applySelectedCustomer();
|
||||||
if (pet.getLabel().equals(adoption.getPetName())) {
|
applySelectedEmployee();
|
||||||
cbPet.getSelectionModel().select(pet);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DropdownOption customer : cbCustomer.getItems()) {
|
|
||||||
if (customer.getLabel().equals(adoption.getCustomerName())) {
|
|
||||||
cbCustomer.getSelectionModel().select(customer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adoption.getEmployeeId() > 0) {
|
|
||||||
for (DropdownOption employee : cbEmployee.getItems()) {
|
|
||||||
if (employee.getId() != null && employee.getId().equals(adoption.getEmployeeId())) {
|
|
||||||
cbEmployee.getSelectionModel().select(employee);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adoption.getAdoptionDate() != null && !adoption.getAdoptionDate().isEmpty()) {
|
if (adoption.getAdoptionDate() != null && !adoption.getAdoptionDate().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
@@ -280,4 +269,46 @@ public class AdoptionDialogController {
|
|||||||
lblMode.setText(mode + " Adoption");
|
lblMode.setText(mode + " Adoption");
|
||||||
lblAdoptionId.setVisible(mode.equals("Edit"));
|
lblAdoptionId.setVisible(mode.equals("Edit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applySelectedPet() {
|
||||||
|
if (selectedAdoption == null || selectedAdoption.getPetId() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DropdownOption selected = findOptionById(cbPet.getItems(), (long) selectedAdoption.getPetId());
|
||||||
|
if (selected != null && !Objects.equals(cbPet.getValue(), selected)) {
|
||||||
|
cbPet.setValue(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySelectedCustomer() {
|
||||||
|
if (selectedAdoption == null || selectedAdoption.getCustomerId() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DropdownOption selected = findOptionById(cbCustomer.getItems(), (long) selectedAdoption.getCustomerId());
|
||||||
|
if (selected != null && !Objects.equals(cbCustomer.getValue(), selected)) {
|
||||||
|
cbCustomer.setValue(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySelectedEmployee() {
|
||||||
|
if (selectedAdoption == null || selectedAdoption.getEmployeeId() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DropdownOption selected = findOptionById(cbEmployee.getItems(), (long) selectedAdoption.getEmployeeId());
|
||||||
|
if (selected != null && !Objects.equals(cbEmployee.getValue(), selected)) {
|
||||||
|
cbEmployee.setValue(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DropdownOption findOptionById(List<DropdownOption> options, Long id) {
|
||||||
|
if (id == null || options == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (DropdownOption option : options) {
|
||||||
|
if (option.getId() != null && option.getId().equals(id)) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import org.example.petshopdesktop.util.ActivityLogger;
|
|||||||
|
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class AppointmentDialogController {
|
public class AppointmentDialogController {
|
||||||
|
|
||||||
@@ -75,49 +76,6 @@ public class AppointmentDialogController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
List<DropdownOption> services = DropdownApi.getInstance().getServices();
|
|
||||||
List<DropdownOption> customers = DropdownApi.getInstance().getCustomers();
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (services != null) {
|
|
||||||
cbService.setItems(FXCollections.observableArrayList(services));
|
|
||||||
}
|
|
||||||
if (customers != null) {
|
|
||||||
cbCustomer.setItems(FXCollections.observableArrayList(customers));
|
|
||||||
}
|
|
||||||
syncSelectedAppointment();
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
ActivityLogger.getInstance().logException(
|
|
||||||
"AppointmentDialogController.initialize",
|
|
||||||
e,
|
|
||||||
"Loading services/customers for appointment dialog");
|
|
||||||
e.printStackTrace();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
Long storeId = UserSession.getInstance().getStoreId();
|
|
||||||
List<DropdownOption> employees = storeId != null && storeId > 0 ? DropdownApi.getInstance().getStoreEmployees(storeId) : List.of();
|
|
||||||
Platform.runLater(() -> cbEmployee.setItems(FXCollections.observableArrayList(employees)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
ActivityLogger.getInstance().logException(
|
|
||||||
"AppointmentDialogController.initialize",
|
|
||||||
e,
|
|
||||||
"Loading employees for appointment dialog");
|
|
||||||
cbEmployee.setDisable(true);
|
|
||||||
cbEmployee.setPromptText("Unable to load employees");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
cbAppointmentStatus.setItems(statusList);
|
cbAppointmentStatus.setItems(statusList);
|
||||||
cbPet.setDisable(true);
|
cbPet.setDisable(true);
|
||||||
cbEmployee.setPromptText("Select an employee");
|
cbEmployee.setPromptText("Select an employee");
|
||||||
@@ -211,6 +169,10 @@ public class AppointmentDialogController {
|
|||||||
|
|
||||||
btnSave.setOnMouseClicked(this::buttonSaveClicked);
|
btnSave.setOnMouseClicked(this::buttonSaveClicked);
|
||||||
btnCancel.setOnMouseClicked(this::closeStage);
|
btnCancel.setOnMouseClicked(this::closeStage);
|
||||||
|
|
||||||
|
loadServices();
|
||||||
|
loadAppointmentCustomers();
|
||||||
|
loadEmployees();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -221,6 +183,7 @@ public class AppointmentDialogController {
|
|||||||
|
|
||||||
selectedAppointment = appt;
|
selectedAppointment = appt;
|
||||||
lblAppointmentId.setText("ID: " + appt.getAppointmentId());
|
lblAppointmentId.setText("ID: " + appt.getAppointmentId());
|
||||||
|
pendingPetSelectionId = appt.getPetId() > 0 ? (long) appt.getPetId() : null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dpAppointmentDate.setValue(
|
dpAppointmentDate.setValue(
|
||||||
@@ -246,24 +209,9 @@ public class AppointmentDialogController {
|
|||||||
"Parsing appointment time");
|
"Parsing appointment time");
|
||||||
}
|
}
|
||||||
|
|
||||||
cbService.getItems().forEach(s -> {
|
applySelectedService();
|
||||||
if (s.getId() != null && s.getId().longValue() == appt.getServiceId()) cbService.setValue(s);
|
applySelectedCustomer();
|
||||||
});
|
applySelectedEmployee();
|
||||||
|
|
||||||
cbCustomer.getItems().forEach(c -> {
|
|
||||||
if (c.getId() != null && c.getId().longValue() == appt.getCustomerId()) {
|
|
||||||
pendingPetSelectionId = (long) appt.getPetId();
|
|
||||||
cbCustomer.setValue(c);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (appt.getEmployeeId() > 0) {
|
|
||||||
cbEmployee.getItems().forEach(employee -> {
|
|
||||||
if (employee.getId() != null && employee.getId().longValue() == appt.getEmployeeId()) {
|
|
||||||
cbEmployee.setValue(employee);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -350,6 +298,49 @@ public class AppointmentDialogController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applySelectedService() {
|
||||||
|
if (selectedAppointment == null || selectedAppointment.getServiceId() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DropdownOption selected = findOptionById(cbService.getItems(), (long) selectedAppointment.getServiceId());
|
||||||
|
if (selected != null && !Objects.equals(cbService.getValue(), selected)) {
|
||||||
|
cbService.setValue(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySelectedCustomer() {
|
||||||
|
if (selectedAppointment == null || selectedAppointment.getCustomerId() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownOption selected = findOptionById(cbCustomer.getItems(), (long) selectedAppointment.getCustomerId());
|
||||||
|
if (selected != null && !Objects.equals(cbCustomer.getValue(), selected)) {
|
||||||
|
cbCustomer.setValue(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySelectedEmployee() {
|
||||||
|
if (selectedAppointment == null || selectedAppointment.getEmployeeId() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DropdownOption selected = findOptionById(cbEmployee.getItems(), (long) selectedAppointment.getEmployeeId());
|
||||||
|
if (selected != null && !Objects.equals(cbEmployee.getValue(), selected)) {
|
||||||
|
cbEmployee.setValue(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DropdownOption findOptionById(List<DropdownOption> options, Long id) {
|
||||||
|
if (id == null || options == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (DropdownOption option : options) {
|
||||||
|
if (option.getId() != null && option.getId().equals(id)) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadCustomerPets(Long customerId) {
|
private void loadCustomerPets(Long customerId) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
@@ -359,22 +350,12 @@ public class AppointmentDialogController {
|
|||||||
cbPet.setDisable(pets == null || pets.isEmpty());
|
cbPet.setDisable(pets == null || pets.isEmpty());
|
||||||
cbPet.setPromptText(pets == null || pets.isEmpty() ? "No pets for selected customer" : "Select a pet");
|
cbPet.setPromptText(pets == null || pets.isEmpty() ? "No pets for selected customer" : "Select a pet");
|
||||||
if (pendingPetSelectionId != null) {
|
if (pendingPetSelectionId != null) {
|
||||||
boolean matched = false;
|
|
||||||
for (DropdownOption pet : cbPet.getItems()) {
|
for (DropdownOption pet : cbPet.getItems()) {
|
||||||
if (pet.getId() != null && pet.getId().equals(pendingPetSelectionId)) {
|
if (pet.getId() != null && pet.getId().equals(pendingPetSelectionId)) {
|
||||||
cbPet.setValue(pet);
|
cbPet.setValue(pet);
|
||||||
matched = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!matched && selectedAppointment != null && selectedAppointment.getPetName() != null && !selectedAppointment.getPetName().isBlank()) {
|
|
||||||
DropdownOption legacy = new DropdownOption();
|
|
||||||
legacy.setId(pendingPetSelectionId);
|
|
||||||
legacy.setLabel(selectedAppointment.getPetName() + " (legacy appointment pet)");
|
|
||||||
cbPet.getItems().add(0, legacy);
|
|
||||||
cbPet.setValue(legacy);
|
|
||||||
cbPet.setDisable(false);
|
|
||||||
}
|
|
||||||
pendingPetSelectionId = null;
|
pendingPetSelectionId = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -391,4 +372,78 @@ public class AppointmentDialogController {
|
|||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadServices() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> services = DropdownApi.getInstance().getServices();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cbService.setItems(FXCollections.observableArrayList(services));
|
||||||
|
applySelectedService();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
ActivityLogger.getInstance().logException(
|
||||||
|
"AppointmentDialogController.loadServices",
|
||||||
|
e,
|
||||||
|
"Loading services for appointment dialog");
|
||||||
|
cbService.setDisable(true);
|
||||||
|
cbService.setPromptText("Unable to load services");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAppointmentCustomers() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> customers = DropdownApi.getInstance().getAppointmentCustomers();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cbCustomer.setItems(FXCollections.observableArrayList(customers));
|
||||||
|
boolean hasCustomers = customers != null && !customers.isEmpty();
|
||||||
|
cbCustomer.setDisable(!hasCustomers);
|
||||||
|
cbPet.setDisable(true);
|
||||||
|
cbPet.setItems(FXCollections.observableArrayList());
|
||||||
|
cbCustomer.setPromptText(hasCustomers ? "Select a customer" : "No customers with pets yet");
|
||||||
|
cbPet.setPromptText(hasCustomers ? "Select a customer first" : "No customer pets available");
|
||||||
|
applySelectedCustomer();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
ActivityLogger.getInstance().logException(
|
||||||
|
"AppointmentDialogController.loadAppointmentCustomers",
|
||||||
|
e,
|
||||||
|
"Loading appointment customers for appointment dialog");
|
||||||
|
cbCustomer.setDisable(true);
|
||||||
|
cbPet.setDisable(true);
|
||||||
|
cbCustomer.setPromptText("Unable to load customers");
|
||||||
|
cbPet.setPromptText("Unable to load pets");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadEmployees() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Long storeId = UserSession.getInstance().getStoreId();
|
||||||
|
List<DropdownOption> employees = storeId != null && storeId > 0
|
||||||
|
? DropdownApi.getInstance().getStoreEmployees(storeId)
|
||||||
|
: List.of();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cbEmployee.setItems(FXCollections.observableArrayList(employees));
|
||||||
|
applySelectedEmployee();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
ActivityLogger.getInstance().logException(
|
||||||
|
"AppointmentDialogController.loadEmployees",
|
||||||
|
e,
|
||||||
|
"Loading employees for appointment dialog");
|
||||||
|
cbEmployee.setDisable(true);
|
||||||
|
cbEmployee.setPromptText("Unable to load employees");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user