diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp index d892e36..28441fb 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp @@ -234,6 +234,17 @@ void Controller::removeInventoryItem(const std::string& inventoryItemID) m_inventoryManagementService.removeInventoryItem(inventoryItemID); } +/* +Function: removeServiceBooking +Description: Removes a service booking from the service management system by its booking ID. +Parameter: const std::string& bookingID - ID of the service booking +Return type: void +*/ +void Controller::removeServiceBooking(const std::string& bookingID) +{ + m_serviceManagementService.removeServiceBooking(bookingID); +} + /* Function: addInventoryItemStock Description: Adds stock to an existing inventory item in the inventory management service. @@ -265,6 +276,7 @@ util::Map Controller::getServiceBookings() return readOnlyServiceBookings; } + /* Function: getServiceBookingsByUser Description: Retrieves all service bookings for a specific user. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h index 622c7b7..2120fe5 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h @@ -58,6 +58,7 @@ public: util::Map getUsers(util::UserType userType); void createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID); void createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost); + void removeServiceBooking(const std::string& bookingID); void removeService(const std::string& serviceID); util::Map getJobCardsByUser(); void updateJobStatus(const std::string& jobID); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index b77ad3d..2d0d153 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -415,7 +415,7 @@ util::Map>& DataStore::getJobCards() { throw std::runtime_error("Invalid booking ID: " + bookingId); } - auto trackedBooking = serviceBookings.getValueAt(bookingIndex); + auto& trackedBooking = serviceBookings.getValueAt(bookingIndex); jobCard->setBooking(trackedBooking.data); const std::string& serviceId = jobCard->getServiceId(); int serviceIndex = services.find(serviceId); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index e975ac5..5e0b30d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -26,7 +26,6 @@ Date:19-May-2026 #include "UserManagementService.h" #include "DataStoreLockGuard.h" #include "Utility.h" -#include "DataStoreLockGuard.h" #include "EventManager.h" /* @@ -141,6 +140,7 @@ void ServiceManagementService::purchaseComboPackage(const std::string& comboPack std::string title = "Combo Package Service Booking succeeded"; std::string message = "Your service booking for the combo package has been successfully placed with ID " + serviceBooking->getId(); sendNotification(authenticatedUser, title, message); + m_dataStore.saveServiceBookings(); notifyAllAdmins("New Combo Package Order Available", "A new combo package order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this); } @@ -629,11 +629,23 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const DataStoreLockGuard lock(m_dataStore); UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); + if (currentBooking == nullptr) + { + throw std::runtime_error("Service Booking not available"); + } + if (currentBooking->getStatus() == util::ServiceJobStatus::CANCELLED) + { + throw std::runtime_error("Cannot create job card. Service Booking was cancelled!"); + } auto& currentTrackedJobCards = m_dataStore.getJobCards(); - if (currentBooking == nullptr) - { - throw std::runtime_error("Service Booking not available"); - } + auto& currentTrackedInventoryItems = m_dataStore.getInventoryItems(); + auto& currentTrackedServiceBookings = m_dataStore.getServiceBookings(); + int currentTrackedServiceBookingIndex = currentTrackedServiceBookings.find(bookingID); + if (currentTrackedServiceBookingIndex == -1) + { + throw std::runtime_error("Invalid service booking id."); + } + auto& currentTrackedServiceBooking = currentTrackedServiceBookings.getValueAt(currentTrackedServiceBookingIndex); auto& currentServices = currentBooking->getServices(); if (currentServices.find(serviceID) == -1) { @@ -658,10 +670,18 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const for (int iterator = 0; iterator < inventoryItems.getSize(); iterator++) { InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iterator); + const std::string& currentInventoryItemId = inventoryItems.getKeyAt(iterator); if (currentInventoryItem) { + int trackedCurrentInventoryItemIndex = currentTrackedInventoryItems.find(currentInventoryItemId); + if (trackedCurrentInventoryItemIndex == -1) + { + throw std::runtime_error("Invalid inventory item index."); + } + auto& trackedCurrentInventoryItem = currentTrackedInventoryItems.getValueAt(trackedCurrentInventoryItemIndex); int currentStockQuantity = currentInventoryItem->getQuantity(); currentInventoryItem->setQuantity(currentStockQuantity - 1); + trackedCurrentInventoryItem.state = RecordState::MODIFIED; } } currentBooking->setAssignedTechnician(selectedTechnician); @@ -670,6 +690,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const { currentBooking->setStatus(util::ServiceJobStatus::STARTED); } + currentTrackedServiceBooking.state = RecordState::MODIFIED; std::string title = "Job card created"; std::string message = "Job card created for the service and you are assigned for that."; JobCard* jobCard = Factory::getObject(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp()); @@ -686,6 +707,8 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const message = "A technician has been assigned to your Service Booking with ID " + bookingID; sendNotification(currentBooking->getCustomer(), title, message); m_dataStore.saveJobCards(); + m_dataStore.saveServiceBookings(); + m_dataStore.saveInventoryItems(); } /* @@ -807,6 +830,61 @@ void ServiceManagementService::removeService(const std::string& serviceID) m_dataStore.saveComboPackages(); } +/* +Function: removeServiceBooking +Description: Removes a pending service booking by its ID. + Cancels only if status is PENDING, otherwise throws exceptions + for invalid states. Sends notification to the customer and + persists changes. +Parameter: const std::string& bookingID - ID of the service booking +Return type: void +*/ +void ServiceManagementService::removeServiceBooking(const std::string& bookingID) +{ + DataStoreLockGuard lock(m_dataStore); + auto& trackedServiceBookings = m_dataStore.getServiceBookings(); + bool serviceBookingRemoved = false; + for (int iterator = 0; iterator < trackedServiceBookings.getSize(); iterator++) + { + auto& currentTrackedServiceBooking = trackedServiceBookings.getValueAt(iterator); + ServiceBooking* currentServiceBooking = currentTrackedServiceBooking.data; + if (currentServiceBooking && currentServiceBooking->getId() == bookingID) + { + if (currentServiceBooking->getStatus() == util::ServiceJobStatus::PENDING) + { + const std::string title = "Service Booking cancelled."; + const std::string message = "Service Booking of id " + bookingID + " successfully cancelled."; + currentServiceBooking->setStatus(util::ServiceJobStatus::CANCELLED); + currentTrackedServiceBooking.state = RecordState::MODIFIED; + serviceBookingRemoved = true; + sendNotification(currentServiceBooking->getCustomer(), title, message); + break; + } + else if(currentServiceBooking->getStatus() == util::ServiceJobStatus::COMPLETED) + { + throw std::runtime_error("Unable to cancel completed service booking."); + } + else if (currentServiceBooking->getStatus() == util::ServiceJobStatus::STARTED) + { + throw std::runtime_error("Unable to cancel started service booking."); + } + else if (currentServiceBooking->getStatus() == util::ServiceJobStatus::IN_PROGRESS) + { + throw std::runtime_error("Unable to cancel currently Inprogress service booking."); + } + else + { + throw std::runtime_error("Service Booking already cancelled."); + } + } + } + if (!serviceBookingRemoved) + { + throw std::runtime_error("Unable to cancel service booking."); + } + m_dataStore.saveServiceBookings(); +} + /* Function: getServiceBookings (overloaded) Description: Retrieves all service bookings for a specific customer. @@ -901,44 +979,44 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID) { AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); - AuthenticationManagementService authenticationManagementService; - PaymentManagementService paymentManagementService; - bool jobStatusUpdated = false, serviceBookingCompleted; - JobCard* currentJob; - User* currentTechnician = authenticationManagementService.getAuthenticatedUser(); - if (currentTechnician == nullptr) - { - throw std::runtime_error("Unable to fetch current technician."); - } - util::Map currentAssignedJobs = getJobCards(currentTechnician->getId()); - if (currentAssignedJobs.getSize() == 0) - { - throw std::runtime_error("No job cards assigned to the technician."); - } + AuthenticationManagementService authenticationManagementService; + PaymentManagementService paymentManagementService; + bool jobStatusUpdated = false, serviceBookingCompleted; + User* currentTechnician = authenticationManagementService.getAuthenticatedUser(); + if (currentTechnician == nullptr) + { + throw std::runtime_error("Unable to fetch current technician."); + } + util::Map currentAssignedJobs = getJobCards(currentTechnician->getId()); + if (currentAssignedJobs.getSize() == 0) + { + throw std::runtime_error("No job cards assigned to the technician."); + } auto& trackedJobCards = m_dataStore.getJobCards(); auto& trackedServiceBookings = m_dataStore.getServiceBookings(); - if (currentAssignedJobs.find(jobID) != -1) - { + if (currentAssignedJobs.find(jobID) != -1) + { int jobIndex = trackedJobCards.find(jobID); if (jobIndex == -1) { throw std::runtime_error("Unable to fetch current job."); } - currentJob = currentAssignedJobs.getValueAt(currentAssignedJobs.find(jobID)); - if (currentJob == nullptr) - { - throw std::runtime_error("Unable to fetch current job."); - } - if (currentJob->getStatus() == util::ServiceJobStatus::STARTED) - { - currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS); - trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED; - jobStatusUpdated = true; - } + auto& trackedCurrentJob = trackedJobCards.getValueAt(jobIndex); + JobCard* currentJob = trackedCurrentJob.data; + if (currentJob == nullptr) + { + throw std::runtime_error("Unable to fetch current job."); + } + if (currentJob->getStatus() == util::ServiceJobStatus::STARTED) + { + currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS); + trackedCurrentJob.state = RecordState::MODIFIED; + jobStatusUpdated = true; + } else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS) { currentJob->setStatus(util::ServiceJobStatus::COMPLETED); - trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED; + trackedCurrentJob.state = RecordState::MODIFIED; jobStatusUpdated = true; serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); if (serviceBookingCompleted) @@ -952,15 +1030,15 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID) sendNotification(currentJob->getBooking()->getCustomer(), title, message); } } - } - else - { - throw std::runtime_error("Failed to update job status. Job may already be completed."); - } - if (!jobStatusUpdated) - { - throw std::runtime_error("Failed to update job status. Job may already be completed."); - } + } + else + { + throw std::runtime_error("Failed to update job status. Job may already be completed."); + } + if (!jobStatusUpdated) + { + throw std::runtime_error("Failed to update job status. Job may already be completed."); + } m_dataStore.saveJobCards(); m_dataStore.saveServiceBookings(); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h index 2fcfd6d..7504a29 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h @@ -35,6 +35,7 @@ public: void createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID); void createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost); void removeService(const std::string& serviceID); + void removeServiceBooking(const std::string& bookingID); util::Map getJobCards(const std::string& technicianID); void updateJobStatus(const std::string& jobID); void cancelCustomerServiceBookings(const std::string& customerID); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp index a9d7990..09e145c 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp @@ -51,11 +51,12 @@ void CustomerMenu::showMenu() << "\n3. Update Profile" << "\n4. Change Password" << "\n5. View Service History" - << "\n6. Complete Payments" - << "\n7. View Invoices" - << "\n8. View Notifications" - << "\n9. Configure Notifications" - << "\n10. Logout" + << "\n6. Cancel Service Booking" + << "\n7. Complete Payments" + << "\n8. View Invoices" + << "\n9. View Notifications" + << "\n10. Configure Notifications" + << "\n11. Logout" << "\nEnter a choice: "; util::read(choice); if (!handleOperation(choice)) @@ -103,18 +104,21 @@ bool CustomerMenu::handleOperation(int choice) viewServiceHistory(); break; case 6: - completePayments(); + cancelServiceBooking(); break; case 7: - viewInvoices(); + completePayments(); break; case 8: - viewNotifications(); + viewInvoices(); break; case 9: - configureNotifications(); + viewNotifications(); break; case 10: + configureNotifications(); + break; + case 11: logout(); return false; default: @@ -333,6 +337,46 @@ void CustomerMenu::viewServiceHistory() util::pressEnter(); } +/* +Function: cancelServiceBooking +Description: Allows the customer to cancel a pending service booking. + Displays the list of active bookings, lets the user select one, + and removes it from the system. If no bookings are available, + an appropriate message is shown. +Parameter: None +Return type: void +*/ +void CustomerMenu::cancelServiceBooking() +{ + util::clear(); + std::cout << "Cancel Service Booking\n"; + const User* currentUser = m_controller.getAuthenticatedUser(); + std::string currentUserID = currentUser->getId(); + util::Map serviceBookingsByCurrentUser = m_controller.getServiceBookingsByUser(currentUserID); + util::Map serviceBookingsMap; + auto currentPendingServiceBookings = filterActiveServiceBookings(serviceBookingsByCurrentUser); + int bookingsSize = currentPendingServiceBookings.getSize(); + if (listServiceBookings(currentPendingServiceBookings, bookingsSize, serviceBookingsMap)) + { + const ServiceBooking* selectedService = selectPendingServiceBookings(serviceBookingsMap); + if (selectedService) + { + m_controller.removeServiceBooking(selectedService->getId()); + std::cout << "Cancelled Service booking of id " + selectedService->getId() << std::endl << std::endl; + } + else + { + std::cout << "Invalid service booking index.\n\n"; + return; + } + } + else + { + std::cout << "No pending service bookings available.\n\n"; + } + util::pressEnter(); +} + /* Function: completePayments Description: Allows the customer to complete pending payments for invoices. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h index c619107..9976300 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h @@ -28,5 +28,6 @@ public: void completePayments(); void viewInvoices(); void viewNotifications(); + void cancelServiceBooking(); void configureNotifications(); -}; +}; \ No newline at end of file