updated backend so booked appointment automatically changes to completed
This commit is contained in:
@@ -37,6 +37,7 @@ public class AppointmentDetailFragment extends Fragment {
|
|||||||
|
|
||||||
private long appointmentId = -1;
|
private long appointmentId = -1;
|
||||||
private boolean isEditing = false;
|
private boolean isEditing = false;
|
||||||
|
private boolean isPastAppointment = false;
|
||||||
private long preselectedPetId = -1;
|
private long preselectedPetId = -1;
|
||||||
private long preselectedServiceId = -1;
|
private long preselectedServiceId = -1;
|
||||||
private long preselectedCustomerId = -1;
|
private long preselectedCustomerId = -1;
|
||||||
@@ -161,6 +162,7 @@ public class AppointmentDetailFragment extends Fragment {
|
|||||||
*/
|
*/
|
||||||
private void setupDatePicker() {
|
private void setupDatePicker() {
|
||||||
binding.etAppointmentDate.setOnClickListener(v -> {
|
binding.etAppointmentDate.setOnClickListener(v -> {
|
||||||
|
if (isPastAppointment) return;
|
||||||
Calendar c = Calendar.getInstance();
|
Calendar c = Calendar.getInstance();
|
||||||
DatePickerDialog d = new DatePickerDialog(requireContext(),
|
DatePickerDialog d = new DatePickerDialog(requireContext(),
|
||||||
(dp,y,m,d1) -> binding.etAppointmentDate.setText(
|
(dp,y,m,d1) -> binding.etAppointmentDate.setText(
|
||||||
@@ -376,6 +378,8 @@ public class AppointmentDetailFragment extends Fragment {
|
|||||||
SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, formattedStatus);
|
SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, formattedStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkIfPastAndDisable(a.getAppointmentDate(), time);
|
||||||
|
|
||||||
refreshPetSpinner();
|
refreshPetSpinner();
|
||||||
refreshServiceSpinner();
|
refreshServiceSpinner();
|
||||||
refreshCustomerSpinner();
|
refreshCustomerSpinner();
|
||||||
@@ -387,6 +391,92 @@ public class AppointmentDetailFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the appointment is in the past and disables fields.
|
||||||
|
*/
|
||||||
|
private void checkIfPastAndDisable(String date, String time) {
|
||||||
|
if (date == null || time == null) return;
|
||||||
|
try {
|
||||||
|
Calendar selected = Calendar.getInstance();
|
||||||
|
String[] dateParts = date.split("-");
|
||||||
|
String[] timeParts = time.split(":");
|
||||||
|
selected.set(
|
||||||
|
Integer.parseInt(dateParts[0]),
|
||||||
|
Integer.parseInt(dateParts[1]) - 1,
|
||||||
|
Integer.parseInt(dateParts[2]),
|
||||||
|
Integer.parseInt(timeParts[0]),
|
||||||
|
Integer.parseInt(timeParts[1]),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
Object selectedItem = binding.spinnerAppointmentStatus.getSelectedItem();
|
||||||
|
String currentStatus = selectedItem != null ? selectedItem.toString() : "";
|
||||||
|
|
||||||
|
// If the appointment is already Cancelled, disable all fields
|
||||||
|
if ("Cancelled".equalsIgnoreCase(currentStatus)) {
|
||||||
|
isPastAppointment = true;
|
||||||
|
disableAllExceptStatus();
|
||||||
|
binding.spinnerAppointmentStatus.setEnabled(false);
|
||||||
|
binding.spinnerAppointmentStatus.setAlpha(0.5f);
|
||||||
|
binding.btnSaveAppointment.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the appointment date/time is in the past
|
||||||
|
if (selected.before(Calendar.getInstance())) {
|
||||||
|
isPastAppointment = true;
|
||||||
|
disableAllExceptStatus();
|
||||||
|
|
||||||
|
// Make status spinner only have Completed or Missed
|
||||||
|
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus,
|
||||||
|
new String[]{"Completed", "Missed"});
|
||||||
|
|
||||||
|
// Restore selection if it's already one of the valid options
|
||||||
|
if (currentStatus.equals("Completed") || currentStatus.equals("Missed")) {
|
||||||
|
SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, currentStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("APPT_DETAIL", "Error parsing date/time for past check: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables all input fields except the status spinner
|
||||||
|
*/
|
||||||
|
private void disableAllExceptStatus() {
|
||||||
|
binding.spinnerCustomer.setEnabled(false);
|
||||||
|
binding.spinnerStore.setEnabled(false);
|
||||||
|
binding.spinnerPet.setEnabled(false);
|
||||||
|
binding.spinnerService.setEnabled(false);
|
||||||
|
binding.spinnerStaff.setEnabled(false);
|
||||||
|
binding.etAppointmentDate.setEnabled(false);
|
||||||
|
binding.spinnerHour.setEnabled(false);
|
||||||
|
binding.spinnerMinute.setEnabled(false);
|
||||||
|
|
||||||
|
float disabledAlpha = 0.5f;
|
||||||
|
binding.spinnerCustomer.setAlpha(disabledAlpha);
|
||||||
|
binding.spinnerStore.setAlpha(disabledAlpha);
|
||||||
|
binding.spinnerPet.setAlpha(disabledAlpha);
|
||||||
|
binding.spinnerService.setAlpha(disabledAlpha);
|
||||||
|
binding.spinnerStaff.setAlpha(disabledAlpha);
|
||||||
|
binding.etAppointmentDate.setAlpha(disabledAlpha);
|
||||||
|
binding.spinnerHour.setAlpha(disabledAlpha);
|
||||||
|
binding.spinnerMinute.setAlpha(disabledAlpha);
|
||||||
|
|
||||||
|
binding.tvLabelCustomer.setAlpha(disabledAlpha);
|
||||||
|
binding.tvLabelStore.setAlpha(disabledAlpha);
|
||||||
|
binding.tvLabelPet.setAlpha(disabledAlpha);
|
||||||
|
binding.tvLabelService.setAlpha(disabledAlpha);
|
||||||
|
binding.tvLabelStaff.setAlpha(disabledAlpha);
|
||||||
|
binding.tvLabelDate.setAlpha(disabledAlpha);
|
||||||
|
binding.tvLabelTime.setAlpha(disabledAlpha);
|
||||||
|
|
||||||
|
// Keep status enabled
|
||||||
|
binding.spinnerAppointmentStatus.setEnabled(true);
|
||||||
|
binding.spinnerAppointmentStatus.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates input and saves the appointment to the backend.
|
* Validates input and saves the appointment to the backend.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -136,6 +136,7 @@
|
|||||||
|
|
||||||
<!-- Staff -->
|
<!-- Staff -->
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/tvLabelStaff"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Staff"
|
android:text="Staff"
|
||||||
@@ -152,6 +153,7 @@
|
|||||||
<!-- Appointment Date -->
|
<!-- Appointment Date -->
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/tvLabelDate"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Appointment Date"
|
android:text="Appointment Date"
|
||||||
@@ -172,6 +174,7 @@
|
|||||||
|
|
||||||
<!-- Appointment Time-->
|
<!-- Appointment Time-->
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/tvLabelTime"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Appointment Time"
|
android:text="Appointment Time"
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import com.petshop.backend.config.FlywayContextInitializer;
|
|||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.data.web.config.EnableSpringDataWebSupport;
|
import org.springframework.data.web.config.EnableSpringDataWebSupport;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
|
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
|
||||||
public class BackendApplication {
|
public class BackendApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
|
import com.petshop.backend.service.AppointmentService;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ApplicationStartupListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||||
|
|
||||||
|
private final AppointmentService appointmentService;
|
||||||
|
|
||||||
|
public ApplicationStartupListener(AppointmentService appointmentService) {
|
||||||
|
this.appointmentService = appointmentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||||
|
//update booked appointments to complete on startup
|
||||||
|
appointmentService.updatePastAppointmentsStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,4 +47,7 @@ public interface AppointmentRepository extends JpaRepository<Appointment, Long>
|
|||||||
|
|
||||||
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.id IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
|
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.id IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
|
||||||
List<Appointment> findByEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
|
List<Appointment> findByEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
|
||||||
|
|
||||||
|
@Query("SELECT a FROM Appointment a WHERE (a.appointmentDate < :currentDate OR (a.appointmentDate = :currentDate AND a.appointmentTime < :currentTime)) AND LOWER(a.appointmentStatus) = 'booked'")
|
||||||
|
List<Appointment> findPastBookedAppointments(@Param("currentDate") LocalDate currentDate, @Param("currentTime") LocalTime currentTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.petshop.backend.repository.UserRepository;
|
|||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -209,6 +210,20 @@ public class AppointmentService {
|
|||||||
return availableSlots;
|
return availableSlots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update booked status to completed at every midnight
|
||||||
|
@Scheduled(cron = "0 0 0 * * ?")
|
||||||
|
@Transactional
|
||||||
|
public void updatePastAppointmentsStatus() {
|
||||||
|
LocalDate currentDate = LocalDate.now();
|
||||||
|
LocalTime currentTime = LocalTime.now();
|
||||||
|
List<Appointment> pastBookedAppointments = appointmentRepository.findPastBookedAppointments(currentDate, currentTime);
|
||||||
|
|
||||||
|
for (Appointment appointment : pastBookedAppointments) {
|
||||||
|
appointment.setAppointmentStatus("COMPLETED");
|
||||||
|
appointmentRepository.save(appointment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizeFilter(String value) {
|
private String normalizeFilter(String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user