Fix availability checks
This commit is contained in:
@@ -39,4 +39,7 @@ public interface AppointmentRepository extends JpaRepository<Appointment, Long>
|
||||
|
||||
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.employeeId = :employeeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
|
||||
List<Appointment> findByEmployeeEmployeeIdAndAppointmentDate(@Param("employeeId") Long employeeId, @Param("date") LocalDate date);
|
||||
|
||||
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.employeeId IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
|
||||
List<Appointment> findByEmployeeEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
|
||||
}
|
||||
|
||||
@@ -211,6 +211,17 @@ public class AppointmentService {
|
||||
.map(EmployeeStore::getEmployee)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (assignableEmployees.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<Long> employeeIds = assignableEmployees.stream().map(Employee::getEmployeeId).collect(Collectors.toList());
|
||||
List<Appointment> allAppointments = appointmentRepository.findByEmployeeEmployeeIdInAndAppointmentDate(employeeIds, date);
|
||||
|
||||
// Group by employee for faster lookup in the loop
|
||||
java.util.Map<Long, List<Appointment>> appointmentsByEmployee = allAppointments.stream()
|
||||
.collect(Collectors.groupingBy(a -> a.getEmployee().getEmployeeId()));
|
||||
|
||||
List<String> availableSlots = new ArrayList<>();
|
||||
LocalTime startTime = LocalTime.of(9, 0);
|
||||
LocalTime endTime = LocalTime.of(17, 0);
|
||||
@@ -220,7 +231,7 @@ public class AppointmentService {
|
||||
while (!currentTime.isAfter(latestStart)) {
|
||||
final LocalTime slotTime = currentTime;
|
||||
boolean anyEmployeeAvailable = assignableEmployees.stream().anyMatch(emp -> {
|
||||
List<Appointment> empAppointments = appointmentRepository.findByEmployeeEmployeeIdAndAppointmentDate(emp.getEmployeeId(), date);
|
||||
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getEmployeeId(), List.of());
|
||||
return isSlotAvailable(empAppointments, service, slotTime, null);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
-- V18: Normalize past appointments.
|
||||
-- Any appointment that is still 'Booked' but the date/time has passed should be marked as 'Missed'.
|
||||
-- V18: Normalize past appointments and resolve initial employee double-bookings
|
||||
|
||||
-- Part 1: Normalize past appointments.
|
||||
-- Any appointment that is still 'Booked' but the date/time has passed should be marked as 'Missed'.
|
||||
UPDATE appointment
|
||||
SET appointmentStatus = 'Missed'
|
||||
WHERE LOWER(appointmentStatus) = 'booked'
|
||||
@@ -8,3 +9,37 @@ WHERE LOWER(appointmentStatus) = 'booked'
|
||||
appointmentDate < CURRENT_DATE
|
||||
OR (appointmentDate = CURRENT_DATE AND appointmentTime < CURRENT_TIME)
|
||||
);
|
||||
|
||||
-- Part 2: Resolve potential double-bookings caused by V15's simple backfill.
|
||||
-- We try to spread overlapping appointments among other active staff in the same store.
|
||||
-- This is a one-time cleanup for demo data integrity.
|
||||
|
||||
-- Temporary table to find conflicts (same employee, same date, overlapping time)
|
||||
-- For simplicity in SQL, we just check exact same time for the demo data cleanup.
|
||||
UPDATE appointment a1
|
||||
SET a1.employeeId = (
|
||||
SELECT es.employeeId
|
||||
FROM employeeStore es
|
||||
JOIN employee e ON e.employeeId = es.employeeId
|
||||
JOIN users u ON u.id = e.user_id
|
||||
WHERE es.storeId = a1.storeId
|
||||
AND e.isActive = TRUE
|
||||
AND u.role = 'STAFF'
|
||||
-- Find an employee who DOES NOT have an appointment at this exact time
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM appointment a2
|
||||
WHERE a2.employeeId = es.employeeId
|
||||
AND a2.appointmentDate = a1.appointmentDate
|
||||
AND a2.appointmentTime = a1.appointmentTime
|
||||
AND a2.appointmentId <> a1.appointmentId
|
||||
)
|
||||
ORDER BY es.employeeId ASC
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM appointment a3
|
||||
WHERE a3.employeeId = a1.employeeId
|
||||
AND a3.appointmentDate = a1.appointmentDate
|
||||
AND a3.appointmentTime = a1.appointmentTime
|
||||
AND a3.appointmentId < a1.appointmentId
|
||||
) AND LOWER(a1.appointmentStatus) NOT IN ('cancelled', 'missed');
|
||||
|
||||
@@ -198,25 +198,4 @@ class DropdownControllerTest {
|
||||
assertEquals(1, response.getBody().size());
|
||||
assertEquals(Long.valueOf(1L), response.getBody().get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAppointmentCustomersReturnsOnlyCustomersWithPetsForAdmin() {
|
||||
User adminUser = new User();
|
||||
adminUser.setId(88L);
|
||||
adminUser.setRole(User.Role.ADMIN);
|
||||
when(userRepository.findById(88L)).thenReturn(Optional.of(adminUser));
|
||||
setAuthentication(88L, User.Role.ADMIN);
|
||||
|
||||
Customer one = new Customer();
|
||||
one.setCustomerId(1L);
|
||||
one.setFirstName("Alex");
|
||||
one.setLastName("Brown");
|
||||
|
||||
when(customerRepository.findAllWithPets()).thenReturn(List.of(one));
|
||||
|
||||
var response = controller.getAppointmentCustomers();
|
||||
|
||||
assertEquals(1, response.getBody().size());
|
||||
assertEquals(Long.valueOf(1L), response.getBody().get(0).getId());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user