From 6f8c0674c2c6c586ac113ebf177e80963def5817 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Mon, 6 Apr 2026 13:26:55 -0600 Subject: [PATCH] Update Postman collection --- .../petstoremobile/dtos/AdoptionDTO.java | 1 - .../petstoremobile/dtos/AppointmentDTO.java | 8 +- backend/petshop-api.postman_collection.json | 132 +++++++++++++++++- .../backend/service/AppointmentService.java | 4 - .../migration/V16__activate_all_employees.sql | 1 - .../V17__normalize_appointment_pets.sql | 6 - .../V18__past_appointments_missed.sql | 6 - .../backend/service/AdoptionServiceTest.java | 2 +- .../petshopdesktop/DTOs/AppointmentDTO.java | 2 - .../controllers/AdoptionController.java | 8 +- .../controllers/AppointmentController.java | 8 +- .../AdoptionDialogController.java | 4 - .../AppointmentDialogController.java | 32 +---- 13 files changed, 135 insertions(+), 79 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java b/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java index 6866f6b0..daf7d768 100644 --- a/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java +++ b/android/app/src/main/java/com/example/petstoremobile/dtos/AdoptionDTO.java @@ -16,7 +16,6 @@ public class AdoptionDTO { private String createdAt; private String updatedAt; - // Constructor for create/update requests public AdoptionDTO(Long petId, Long customerId, String adoptionDate, String adoptionStatus) { this(petId, customerId, null, adoptionDate, adoptionStatus); } diff --git a/android/app/src/main/java/com/example/petstoremobile/dtos/AppointmentDTO.java b/android/app/src/main/java/com/example/petstoremobile/dtos/AppointmentDTO.java index 05f9ea21..01f8ef5d 100644 --- a/android/app/src/main/java/com/example/petstoremobile/dtos/AppointmentDTO.java +++ b/android/app/src/main/java/com/example/petstoremobile/dtos/AppointmentDTO.java @@ -4,7 +4,7 @@ import java.math.BigDecimal; import java.util.List; public class AppointmentDTO { - // Response fields (from server) + private Long appointmentId; private Long customerId; private String customerName; @@ -22,8 +22,6 @@ public class AppointmentDTO { private String createdAt; private String updatedAt; - // Constructor for CREATE/UPDATE request body - // Matches AppointmentRequest exactly public AppointmentDTO(Long customerId, Long storeId, Long serviceId, String appointmentDate, String appointmentTime, String appointmentStatus, List petIds) { @@ -43,7 +41,6 @@ public class AppointmentDTO { this.petIds = petIds; } - // Getters public Long getAppointmentId() { return appointmentId; } @@ -108,7 +105,6 @@ public class AppointmentDTO { return updatedAt; } - // Convenience getters for adapter/list display public String getPetName() { return (petNames != null && !petNames.isEmpty()) ? petNames.get(0) : ""; } @@ -121,7 +117,6 @@ public class AppointmentDTO { return getPetID(); } - // Keep old name so adapter doesn't break public String getServiceType() { return serviceName; } @@ -130,7 +125,6 @@ public class AppointmentDTO { return serviceId; } - // Status alias public String getStatus() { return appointmentStatus; } diff --git a/backend/petshop-api.postman_collection.json b/backend/petshop-api.postman_collection.json index 7db7d61f..73fed551 100644 --- a/backend/petshop-api.postman_collection.json +++ b/backend/petshop-api.postman_collection.json @@ -2069,6 +2069,37 @@ { "name": "Appointments", "item": [ + { + "name": "Get Appointment Customers Dropdown", + "request": { + "method": "GET", + "url": "{{baseUrl}}/api/v1/dropdowns/appointment-customers", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{staffToken}}", + "type": "text" + } + ] + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Status code is 200', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] + }, { "name": "Check Appointment Availability", "request": { @@ -2180,7 +2211,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"customerId\": 1,\n \"storeId\": 1,\n \"serviceId\": 1,\n \"appointmentDate\": \"2026-12-20\",\n \"appointmentTime\": \"10:00:00\",\n \"appointmentStatus\": \"Booked\",\n \"petIds\": [1]\n}", + "raw": "{\n \"customerId\": 1,\n \"storeId\": 1,\n \"serviceId\": 1,\n \"appointmentDate\": \"2026-12-20\",\n \"appointmentTime\": \"10:00:00\",\n \"appointmentStatus\": \"Booked\",\n \"petIds\": [\n 1\n ],\n \"employeeId\": 1\n}", "options": { "raw": { "language": "json" @@ -2222,7 +2253,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"customerId\": 1,\n \"storeId\": 1,\n \"serviceId\": 1,\n \"appointmentDate\": \"2026-12-20\",\n \"appointmentTime\": \"11:00:00\",\n \"appointmentStatus\": \"Booked\",\n \"petIds\": [1]\n}", + "raw": "{\n \"customerId\": 1,\n \"storeId\": 1,\n \"serviceId\": 1,\n \"appointmentDate\": \"2026-12-20\",\n \"appointmentTime\": \"11:00:00\",\n \"appointmentStatus\": \"Booked\",\n \"petIds\": [\n 1\n ],\n \"employeeId\": 1\n}", "options": { "raw": { "language": "json" @@ -2315,6 +2346,37 @@ { "name": "Adoptions", "item": [ + { + "name": "Get Adoption Pets Dropdown", + "request": { + "method": "GET", + "url": "{{baseUrl}}/api/v1/dropdowns/adoption-pets", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{staffToken}}", + "type": "text" + } + ] + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Status code is 200', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] + }, { "name": "List Adoptions", "request": { @@ -2395,7 +2457,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"petId\": 3,\n \"customerId\": 1,\n \"adoptionDate\": \"2026-12-21\",\n \"adoptionStatus\": \"Pending\"\n}", + "raw": "{\n \"petId\": 3,\n \"customerId\": 1,\n \"adoptionDate\": \"2026-12-21\",\n \"adoptionStatus\": \"Pending\",\n \"employeeId\": 1\n}", "options": { "raw": { "language": "json" @@ -2437,7 +2499,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"petId\": 3,\n \"customerId\": 1,\n \"adoptionDate\": \"2026-12-22\",\n \"adoptionStatus\": \"Completed\"\n}", + "raw": "{\n \"petId\": 3,\n \"customerId\": 1,\n \"adoptionDate\": \"2026-12-22\",\n \"adoptionStatus\": \"Completed\",\n \"employeeId\": 1\n}", "options": { "raw": { "language": "json" @@ -3719,6 +3781,68 @@ } ] }, + { + "name": "Get Store Employees Dropdown", + "request": { + "method": "GET", + "url": "{{baseUrl}}/api/v1/dropdowns/stores/{{storeId}}/employees", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{staffToken}}", + "type": "text" + } + ] + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Status code is 200', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] + }, + { + "name": "Get All Employees Dropdown", + "request": { + "method": "GET", + "url": "{{baseUrl}}/api/v1/dropdowns/employees", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{staffToken}}", + "type": "text" + } + ] + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Status code is 200', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] + }, { "name": "List Stores", "request": { diff --git a/backend/src/main/java/com/petshop/backend/service/AppointmentService.java b/backend/src/main/java/com/petshop/backend/service/AppointmentService.java index b4e270fa..155b7524 100644 --- a/backend/src/main/java/com/petshop/backend/service/AppointmentService.java +++ b/backend/src/main/java/com/petshop/backend/service/AppointmentService.java @@ -218,7 +218,6 @@ public class AppointmentService { List employeeIds = assignableEmployees.stream().map(Employee::getEmployeeId).collect(Collectors.toList()); List allAppointments = appointmentRepository.findByEmployeeEmployeeIdInAndAppointmentDate(employeeIds, date); - // Group by employee for faster lookup in the loop java.util.Map> appointmentsByEmployee = allAppointments.stream() .collect(Collectors.groupingBy(a -> a.getEmployee().getEmployeeId())); @@ -350,7 +349,6 @@ public class AppointmentService { .isPresent(); } - //------------------------------------ private void validateAvailability(Employee employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) { List existingAppointments = appointmentRepository .findByEmployeeEmployeeIdAndAppointmentDate(employee.getEmployeeId(), date); @@ -359,8 +357,6 @@ public class AppointmentService { } } - //------------------------------------------------ - private boolean isSlotAvailable(List existingAppointments, com.petshop.backend.entity.Service requestedService, LocalTime requestedStart, Long appointmentIdToIgnore) { LocalTime requestedEnd = requestedStart.plusMinutes(requestedService.getServiceDuration()); for (Appointment existingAppointment : existingAppointments) { diff --git a/backend/src/main/resources/db/migration/V16__activate_all_employees.sql b/backend/src/main/resources/db/migration/V16__activate_all_employees.sql index cbabc11d..314c86c8 100644 --- a/backend/src/main/resources/db/migration/V16__activate_all_employees.sql +++ b/backend/src/main/resources/db/migration/V16__activate_all_employees.sql @@ -1,4 +1,3 @@ --- Activate all employees in the users table so they appear in dropdowns UPDATE users u SET u.active = TRUE WHERE u.role IN ('STAFF', 'ADMIN') diff --git a/backend/src/main/resources/db/migration/V17__normalize_appointment_pets.sql b/backend/src/main/resources/db/migration/V17__normalize_appointment_pets.sql index 00d34751..90c2a407 100644 --- a/backend/src/main/resources/db/migration/V17__normalize_appointment_pets.sql +++ b/backend/src/main/resources/db/migration/V17__normalize_appointment_pets.sql @@ -1,7 +1,3 @@ --- V17: Normalize legacy appointmentPet data into customer_pet and appointment_customer_pet - --- Step 1: Ensure a customer_pet exists for every pet linked in appointmentPet --- Note: pet species and breed might be null in pet table, but we copy them over if present INSERT INTO customer_pet (customer_id, pet_name, species, breed) SELECT DISTINCT a.customerId, p.petName, p.petSpecies, p.petBreed FROM appointmentPet ap @@ -12,7 +8,6 @@ WHERE NOT EXISTS ( WHERE cp.customer_id = a.customerId AND cp.pet_name = p.petName ); --- Step 2: Link the appointment to the customer_pet INSERT INTO appointment_customer_pet (appointment_id, customer_pet_id) SELECT ap.appointmentId, cp.customer_pet_id FROM appointmentPet ap @@ -24,5 +19,4 @@ WHERE NOT EXISTS ( WHERE acp.appointment_id = ap.appointmentId AND acp.customer_pet_id = cp.customer_pet_id ); --- Step 3: Remove the old legacy relationships so it strictly uses the new ones DELETE FROM appointmentPet; diff --git a/backend/src/main/resources/db/migration/V18__past_appointments_missed.sql b/backend/src/main/resources/db/migration/V18__past_appointments_missed.sql index 19ba0fa0..093c6ce3 100644 --- a/backend/src/main/resources/db/migration/V18__past_appointments_missed.sql +++ b/backend/src/main/resources/db/migration/V18__past_appointments_missed.sql @@ -1,7 +1,3 @@ --- 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' @@ -10,8 +6,6 @@ WHERE LOWER(appointmentStatus) = 'booked' OR (appointmentDate = CURRENT_DATE AND appointmentTime < CURRENT_TIME) ); --- Part 2: Resolve potential double-bookings caused by V15's simple backfill. --- MySQL Error 1093 workaround: wrap same-table subqueries in derived tables. UPDATE appointment a1 JOIN ( SELECT a3.appointmentId diff --git a/backend/src/test/java/com/petshop/backend/service/AdoptionServiceTest.java b/backend/src/test/java/com/petshop/backend/service/AdoptionServiceTest.java index 1fef0e42..a133c29f 100644 --- a/backend/src/test/java/com/petshop/backend/service/AdoptionServiceTest.java +++ b/backend/src/test/java/com/petshop/backend/service/AdoptionServiceTest.java @@ -90,7 +90,7 @@ class AdoptionServiceTest { void createAdoptionAutoAssignsFirstStaffEmployee() { when(petRepository.findById(1L)).thenReturn(Optional.of(pet)); when(customerRepository.findById(1L)).thenReturn(Optional.of(customer)); - // resolveAdoptionEmployee filters for staff + when(employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc()).thenReturn(List.of(adminEmployee, staffEmployee)); when(adoptionRepository.save(any(Adoption.class))).thenAnswer(invocation -> { Adoption adoption = invocation.getArgument(0); diff --git a/desktop/src/main/java/org/example/petshopdesktop/DTOs/AppointmentDTO.java b/desktop/src/main/java/org/example/petshopdesktop/DTOs/AppointmentDTO.java index 71b7005c..cab8e4cb 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/DTOs/AppointmentDTO.java +++ b/desktop/src/main/java/org/example/petshopdesktop/DTOs/AppointmentDTO.java @@ -22,7 +22,6 @@ public class AppointmentDTO { private SimpleStringProperty appointmentTime; private SimpleStringProperty appointmentStatus; - // Constructor public AppointmentDTO(int appointmentId, int customerId, String customerName, int petId, String petName, @@ -47,7 +46,6 @@ public class AppointmentDTO { this.appointmentStatus = new SimpleStringProperty(appointmentStatus); } - // Getters public int getAppointmentId() { return appointmentId.get(); } public int getCustomerId() { return customerId.get(); } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java index 0b8fe447..4de4d212 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java @@ -68,7 +68,7 @@ public class AdoptionController { void initialize() { btnEdit.setDisable(true); btnDelete.setDisable(true); - //Enable multiple selection + tvAdoptions.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE); colAdoptionId.setCellValueFactory(new PropertyValueFactory<>("adoptionId")); @@ -91,7 +91,6 @@ public class AdoptionController { displayFilteredAdoptions(newValue); }); - //EventListener for DELETE key tvAdoptions.setOnKeyPressed(event -> { if (event.getCode() == javafx.scene.input.KeyCode.DELETE) { if (tvAdoptions.getSelectionModel().getSelectedItem() != null) { @@ -109,11 +108,10 @@ public class AdoptionController { @FXML void btnDeleteClicked(ActionEvent event) { - //get selected adoptions + var selectedAdoptions = tvAdoptions.getSelectionModel().getSelectedItems(); if (selectedAdoptions.isEmpty()) return; - //ask user to confirm Alert question = new Alert(Alert.AlertType.CONFIRMATION); question.setHeaderText("Please confirm delete"); String message = selectedAdoptions.size() == 1 @@ -123,7 +121,6 @@ public class AdoptionController { question.getDialogPane().lookupButton(ButtonType.OK).requestFocus(); Optional result = question.showAndWait(); - //if confirmed, start deletion if (result.isPresent() && result.get() == ButtonType.OK) { List ids = selectedAdoptions.stream() .map(a -> (long) a.getAdoptionId()) @@ -146,7 +143,6 @@ public class AdoptionController { alert.showAndWait(); } - //refresh display and reset inputs displayAdoptions(); btnDelete.setDisable(true); btnEdit.setDisable(true); diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java index ba4c05aa..d183918a 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/AppointmentController.java @@ -47,7 +47,7 @@ public class AppointmentController { @FXML public void initialize(){ - //Enable multiple selection + tvAppointments.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE); colAppointmentId.setCellValueFactory(new PropertyValueFactory<>("appointmentId")); @@ -66,7 +66,6 @@ public class AppointmentController { txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n)); } - //EventListener for DELETE key tvAppointments.setOnKeyPressed(event -> { if (event.getCode() == javafx.scene.input.KeyCode.DELETE) { if (tvAppointments.getSelectionModel().getSelectedItem() != null) { @@ -148,11 +147,10 @@ public class AppointmentController { @FXML void btnDeleteClicked(ActionEvent event){ - //get selected appointments + var selectedAppointments = tvAppointments.getSelectionModel().getSelectedItems(); if (selectedAppointments.isEmpty()) return; - //ask user to confirm Alert question = new Alert(Alert.AlertType.CONFIRMATION); question.setHeaderText("Please confirm delete"); String message = selectedAppointments.size() == 1 @@ -162,7 +160,6 @@ public class AppointmentController { question.getDialogPane().lookupButton(ButtonType.OK).requestFocus(); java.util.Optional result = question.showAndWait(); - //if confirmed, start deletion if (result.isPresent() && result.get() == ButtonType.OK) { List ids = selectedAppointments.stream() .map(a -> (long) a.getAppointmentId()) @@ -185,7 +182,6 @@ public class AppointmentController { alert.showAndWait(); } - //refresh display loadAppointments(); } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java index 56f682c6..67e1073f 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java @@ -28,7 +28,6 @@ import java.util.Objects; public class AdoptionDialogController { - //FXML elements @FXML private Button btnCancel; @@ -56,11 +55,9 @@ public class AdoptionDialogController { @FXML private Label lblMode; - //Stores if the dialog view is in add/edit mode private String mode = null; private Adoption selectedAdoption = null; - //Adoption statuses private ObservableList statusList = FXCollections.observableArrayList( "Pending", "Completed", "Cancelled" ); @@ -234,7 +231,6 @@ public class AdoptionDialogController { } } - private void closeStage(MouseEvent mouseEvent) { Node node = (Node) mouseEvent.getSource(); Stage stage = (Stage) node.getScene().getWindow(); 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 cac4f47e..d21ad756 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 @@ -26,10 +26,6 @@ import java.util.Objects; public class AppointmentDialogController { - // ============================ - // FXML - // ============================ - @FXML private Button btnCancel; @FXML private Button btnSave; @@ -47,11 +43,7 @@ public class AppointmentDialogController { @FXML private Label lblAppointmentId; @FXML private Label lblMode; - // ============================ - // DATA - // ============================ - - private String mode = null; // Add | Edit + private String mode = null; private AppointmentDTO selectedAppointment = null; private Long pendingPetSelectionId = null; @@ -60,20 +52,12 @@ public class AppointmentDialogController { "Booked", "Completed", "Cancelled", "Missed" ); - // - // MODE - // - public void setMode(String mode) { this.mode = mode; lblMode.setText(mode + " Appointment"); lblAppointmentId.setVisible(!mode.equals("Add")); } - // - // INITIALIZE - // - @FXML public void initialize() { cbAppointmentStatus.setItems(statusList); @@ -85,14 +69,12 @@ public class AppointmentDialogController { dpAppointmentDate.setValue(LocalDate.now().plusDays(1)); cbAppointmentStatus.setValue("Booked"); - // Hours 9 AM - 5 PM for (int i = 9; i <= 17; i++) { cbHour.getItems().add(i); } cbMinute.getItems().addAll(0, 15, 30, 45); - // Show dropdown labels cbService.setCellFactory(param -> new ListCell<>() { @Override protected void updateItem(DropdownOption option, boolean empty) { @@ -175,10 +157,6 @@ public class AppointmentDialogController { loadEmployees(); } - // - // DISPLAY FOR EDIT - // - public void displayAppointmentDetails(AppointmentDTO appt) { selectedAppointment = appt; @@ -214,10 +192,6 @@ public class AppointmentDialogController { applySelectedEmployee(); } - // - // SAVE - // - private void buttonSaveClicked(MouseEvent e) { if (cbService.getValue() == null || @@ -276,10 +250,6 @@ public class AppointmentDialogController { }).start(); } - // - // UTIL - // - private void closeStage(MouseEvent e) { Stage stage = (Stage) ((Node) e.getSource()).getScene().getWindow(); stage.close();