From 859f7bbeaa44bea4db589b879cb6c8c449e4b95d Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 27 May 2026 13:25:11 +0530 Subject: [PATCH 1/4] Fix Customer Removal and Cancellation Handling Issues - Refactored customer and technician removal flow to ensure linked job cards and service bookings are properly cancelled. - Added inventory restoration logic to avoid duplicate restocking when cancelling bookings. - Introduced processBookingCancellation helper for consistent cancellation handling, notifications, and technician reassignment. - Updated UserManagementService::removeUser to invoke appropriate cancellation routines based on user type. - Ensured customer references and IDs are preserved correctly during booking cancellation. Fixes #1781 --- .../controllers/Controller.cpp | 4 +- .../services/ServiceManagementService.cpp | 231 ++++++++++++------ .../services/UserManagementService.cpp | 8 + 3 files changed, 164 insertions(+), 79 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp index f1ca400..3b674ed 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp @@ -409,10 +409,8 @@ void Controller::removeUser(const std::string& userID) User* user = m_userManagementService.getUser(userID); if (!user) { - throw std::runtime_error("Error User not Found.\n"); + throw std::runtime_error("Error: User not Found.\n"); } - m_serviceManagementService.cancelCustomerServiceBookings(userID); - m_serviceManagementService.cancelTechnicianJobs(userID); m_userManagementService.removeUser(userID); } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index b4fbc22..da45370 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -507,6 +507,89 @@ void ServiceManagementService::saveObservers() util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this); } +/* +Function: restoreInventory +Description: Restores inventory quantities for all required items in the services associated + with a given booking. Each item's quantity is incremented by a fixed value. +Parameter: ServiceBooking* booking - Pointer to the booking whose inventory items need to be restored +Return type: void +*/ +static void restoreInventory(ServiceBooking* booking) +{ + const int INCREMENT_VALUE = 1; + if (!booking) + { + return; + } + const auto& services = booking->getServices(); + for (int serviceIterator = 0; serviceIterator < services.getSize(); ++serviceIterator) + { + Service* service = services.getValueAt(serviceIterator); + if (!service) + { + continue; + } + const auto& items = service->getRequiredInventoryItems(); + for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++InventoryIterator) + { + InventoryItem* item = items.getValueAt(InventoryIterator); + if (item) + { + item->setQuantity(item->getQuantity() + INCREMENT_VALUE); + } + } + } +} + +/* +Function: processBookingCancellation +Description: Cancels jobs and updates the status of a given booking. Sends notifications to the + specified user, resets technician assignment if needed, and restores inventory items. +Parameter: ServiceBooking* booking - Pointer to the booking being cancelled + util::ServiceJobStatus newServiceBookingStatus - New status to assign to the booking + const std::string& notificationTitle - Title of the booking cancellation notification + const std::string& notificationMessage - Message body of the booking cancellation notification + User* notifyUser - User to notify about the cancellation + util::ServiceJobStatus jobCardStatus - New status to assign to associated job cards + const std::string& jobNotificationTitle - Title of the job cancellation notification + const std::string& jobNotificationMessage - Message body of the job cancellation notification + util::Map& jobs - Collection of job cards to update + ServiceManagementService& currentService - Reference to the service for sending notifications +Return type: void +*/ +static void processBookingCancellation(ServiceBooking* booking, + util::ServiceJobStatus newServiceBookingStatus, + const std::string& notificationTitle, + const std::string& notificationMessage, + User* notifyUser, + util::ServiceJobStatus jobCardStatus, + const std::string& jobNotificationTitle, + const std::string& jobNotificationMessage, + util::Map& jobs, ServiceManagementService& currentService) +{ + if (!booking || !notifyUser) + { + return; + } + for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator) + { + JobCard* jobCard = jobs.getValueAt(jobIterator); + if (jobCard && jobCard->getBookingId() == booking->getId()) + { + jobCard->setStatus(jobCardStatus); + currentService.sendNotification(notifyUser, jobNotificationTitle, jobNotificationMessage); + } + } + booking->setStatus(newServiceBookingStatus); + currentService.sendNotification(notifyUser, notificationTitle, notificationMessage); + if (newServiceBookingStatus == util::ServiceJobStatus::PENDING) + { + booking->setAssignedTechnician(nullptr); + booking->setAssignedTechnicianId(""); + } + restoreInventory(booking); +} + /* Function: cancelCustomerServiceBookings Description: Cancels all service bookings associated with a given customer or technician. @@ -515,69 +598,49 @@ Description: Cancels all service bookings associated with a given customer or te Parameter: const std::string& userID - ID of the customer or technician Return type: void */ -void ServiceManagementService::cancelCustomerServiceBookings(const std::string& userID) +void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID) { const int INCREMENT_VALUE = 1; auto& users = m_dataStore.getUsers(); - int userIndex = users.find(userID); + int userIndex = users.find(customerID); if (userIndex == -1) { - throw std::runtime_error("User not found: " + userID); + throw std::runtime_error("User not found: " + customerID); } - User* user = users.getValueAt(userIndex); - if (user == nullptr) + User* customer = users.getValueAt(userIndex); + if (!customer) { - throw std::runtime_error("User not found: " + userID); + throw std::runtime_error("User not found: " + customerID); } - util::UserType type = user->getUserType(); - auto& bookings = DataStore::getInstance().getServiceBookings(); - for (int bookingIterator = 0; bookingIterator < bookings.getSize(); bookingIterator++) + auto& bookings = m_dataStore.getServiceBookings(); + auto& jobs = m_dataStore.getJobCards(); + for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++) { - ServiceBooking* booking = bookings.getValueAt(bookingIterator); - if (booking != nullptr && - (booking->getCustomerId() == userID || booking->getAssignedTechnicianId() == userID)) + ServiceBooking* booking = bookings.getValueAt(iteratorOne); + if (!booking) { - if (booking->getStatus() == util::ServiceJobStatus::PENDING || - booking->getStatus() == util::ServiceJobStatus::STARTED) - { - if (type == util::UserType::CUSTOMER) - { - booking->setStatus(util::ServiceJobStatus::CANCELLED); - booking->setCustomer(nullptr); - booking->setCustomerId(""); - User* assignedTechnician = booking->getAssignedTechnician(); - std::string title = "Customer Service Cancelled"; - std::string message = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked."; - sendNotification(assignedTechnician, title, message); - } - else if (type == util::UserType::TECHNICIAN) - { - booking->setStatus(util::ServiceJobStatus::PENDING); - std::string title = "Technician Unavailable"; - std::string message = "Your assigned technician is no longer available. Your booking has been reset to pending, and we will reassign a new technician shortly."; - sendNotification(booking->getCustomer(), title, message); - } - booking->setAssignedTechnician(nullptr); - booking->setAssignedTechnicianId(""); - const auto& ListOfServices = booking->getServices(); - for (int serviceIterator = 0; serviceIterator < ListOfServices.getSize(); serviceIterator++) - { - Service* service = ListOfServices.getValueAt(serviceIterator); - if (service != nullptr) - { - const auto& items = service->getRequiredInventoryItems(); - for (int itemIterator = 0; itemIterator < items.getSize(); itemIterator++) - { - InventoryItem* item = items.getValueAt(itemIterator); - if (item != nullptr) - { - item->setQuantity(item->getQuantity() + INCREMENT_VALUE); - } - } - } - } - } + continue; } + std::string bookingID = booking->getId(); + if (booking->getCustomerId() != customerID) + { + continue; + } + if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED) + { + continue; + } + User* assignedTechnician = booking->getAssignedTechnician(); + std::string titleToTechnician = "Customer Service Cancelled"; + std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked."; + std::string jobTitle = "Job Cancelled"; + std::string jobMessage = "The Job has cancelled. Your job card has been cancelled and the inventory has been restocked."; + processBookingCancellation(booking, + util::ServiceJobStatus::CANCELLED, + titleToTechnician, messageToTechnician, assignedTechnician, + util::ServiceJobStatus::CANCELLED, + jobTitle, jobMessage, jobs, *this + ); } } @@ -591,33 +654,49 @@ Return type: void void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID) { const int INCREMENT_VALUE = 1; - auto& jobs = m_dataStore.getJobCards(); - for (int jobIterator = 0; jobIterator < jobs.getSize(); jobIterator++) + auto& users = m_dataStore.getUsers(); + int userIndex = users.find(technicianID); + if (userIndex == -1) { - JobCard* job = jobs.getValueAt(jobIterator); - if (job != nullptr && job->getTechnicianId() == technicianID) + throw std::runtime_error("User not found: " + technicianID); + } + User* technician = users.getValueAt(userIndex); + if (!technician) + { + throw std::runtime_error("User not found: " + technicianID); + } + auto& bookings = m_dataStore.getServiceBookings(); + auto& jobs = m_dataStore.getJobCards(); + for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++) + { + ServiceBooking* booking = bookings.getValueAt(iteratorOne); + if (!booking) { - if (job->getStatus() == util::ServiceJobStatus::PENDING || job->getStatus() == util::ServiceJobStatus::STARTED) - { - job->setStatus(util::ServiceJobStatus::CANCELLED); - std::string title = "Job Cancelled"; - std::string message = "The Job has cancelled. Your job card has been cancelled and the inventory has been restocked."; - sendNotification(job->getTechnician(), title, message); - Service* service = job->getService(); - if (service != nullptr) - { - const auto& items = service->getRequiredInventoryItems(); - for (int itemIterator = 0; itemIterator < items.getSize(); itemIterator++) - { - InventoryItem* item = items.getValueAt(itemIterator); - if (item != nullptr) - { - item->setQuantity(item->getQuantity() + INCREMENT_VALUE); - } - } - } - } + continue; } + std::string technicianId = booking->getAssignedTechnicianId(); + if (technicianId != technicianID) + { + continue; + } + std::string bookingID = booking->getId(); + if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED) + { + continue; + } + User* customer = booking->getCustomer(); + if (!customer) + { + continue; + } + std::string title = "Technician Unavailable"; + std::string message = "Your assigned technician is no longer available. Your booking has been reset to pending, and we will reassign a new technician shortly."; + processBookingCancellation(booking, + util::ServiceJobStatus::PENDING, + title, message, customer, + util::ServiceJobStatus::CANCELLED, + title, message, jobs, *this + ); } } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 84fa7aa..df11b15 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -304,6 +304,14 @@ void UserManagementService::removeUser(const std::string& userID) if (index != -1) { User* user = m_dataStore.getUsers().getValueAt(index); + if (user->getUserType() == util::UserType::CUSTOMER) + { + serviceManagementService.cancelCustomerServiceBookings(userID); + } + if (user->getUserType() == util::UserType::TECHNICIAN) + { + serviceManagementService.cancelTechnicianJobs(userID); + } if (user != nullptr) { user->setState(util::State::INACTIVE); From c1bd2a6ef166f612729a0b6a37e6f22cda896c86 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 27 May 2026 16:16:28 +0530 Subject: [PATCH 2/4] Fix Admin Inventory Management UI Issues and Workflow Enhancements - Added heading output to Remove Inventory Item submenu for clearer user context. - Prevented service creation with empty inventory selection by adding validation and early return. - Improved combo package creation by simplifying null checks with modern idioms. - Enhanced inventory selection flow: - Clear screen and context header for better UX. - Prevent duplicate item selection by checking already chosen items. - Changed exit option from -1 to 0 for consistency. - Added pressEnter prompt after successful item addition. - Simplified pointer checks using concise conditions (e.g., if (item)). Fixes #1778 --- .../views/AdminMenu.cpp | 8 +++++- .../views/MenuHelper.h | 25 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp index bb7e995..2afc1f2 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp @@ -263,6 +263,7 @@ Return type: void void AdminMenu::removeInventoryItem() { util::clear(); + std::cout << "Remove Inventory Item\n"; auto inventoryItems = m_controller.getInventoryItems(); auto activeItems = filterActiveItems(inventoryItems); int activeItemsSize = activeItems.getSize(); @@ -402,6 +403,11 @@ void AdminMenu::createService() util::Map activeInventoryItems = filterActiveItems(currentInventoryItems); util::Vector selectedInventoryItems; selectInventoryItems(activeInventoryItems,selectedInventoryItems); + if (selectedInventoryItems.isEmpty()) + { + util::pressEnter(); + return; + } std::cout << "\nEnter the labour cost: "; util::read(labourCost); m_controller.createService(serviceName, selectedInventoryItems, labourCost); @@ -541,7 +547,7 @@ void AdminMenu::createComboPackages() while (true) { chosenService = selectServiceFromServices(activeServices); - if (chosenService == nullptr) + if (!chosenService) { std::cout << "Failed to create combo package!\n\n"; util::pressEnter(); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 9703042..c929fce 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -94,13 +94,15 @@ inline void selectInventoryItems(util::Map& c bool doRun = true; util::Map currentInventoryMap; int choice; - if (currentInventoryItems.getSize() == 0) + if (currentInventoryItems.isEmpty()) { std::cout << "No Items Present, Inventory empty.\n"; return; } while (doRun) { + util::clear(); + std::cout << "Create Service\n"; std::cout << "\nSelect Required Items\n"; bool hasInventoryItems = false; int currentIndex = 1; @@ -119,6 +121,19 @@ inline void selectInventoryItems(util::Map& c { continue; } + bool alreadySelected = false; + for (int iteratorOne = 0; iteratorOne < selectedInventoryItems.getSize(); iteratorOne++) + { + if (selectedInventoryItems[iteratorOne] == currentInventoryItem->getId()) + { + alreadySelected = true; + break; + } + } + if (alreadySelected) + { + continue; + } std::cout << std::left << std::setw(6) << currentIndex << std::setw(12) << currentInventoryItem->getId() @@ -133,10 +148,9 @@ inline void selectInventoryItems(util::Map& c { break; } - std::cout << "Select the item (Index) or enter -1 to exit: "; + std::cout << "Select the item (Index) or enter 0 to exit: "; util::read(choice); - - if (choice == -1) + if (choice == 0) { doRun = false; } @@ -144,6 +158,7 @@ inline void selectInventoryItems(util::Map& c { selectedInventoryItems.push_back(currentInventoryMap.getValueAt(currentInventoryMap.find(choice))->getId()); std::cout << "Item added successfully.\n" << std::endl; + util::pressEnter(); } else { @@ -1066,7 +1081,7 @@ inline util::Map filterActiveItems(const util for (int index = 0; index < inventorySize; index++) { const InventoryItem* item = inventoryItems.getValueAt(index); - if (item != nullptr && item->getState() != util::State::INACTIVE) + if (item && item->getState() != util::State::INACTIVE) { activeItems.insert(item->getId(), item); } From b25b3d59cf4c4a69aeff3555e6d3954c94d1201d Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 27 May 2026 16:23:42 +0530 Subject: [PATCH 3/4] Fix Admin Menu Remove User Incomplete Data - Updated active user display to include Full Name column. - Ensured both header and row output show full name alongside ID, username, and user type. - Improved clarity of Remove User submenu by presenting complete user information. Fixes #1788 --- .../Trenser.VehicleServiceSystem/views/MenuHelper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index c929fce..d17b731 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -855,6 +855,7 @@ inline void displayAllActiveUsers(util::Map& activeUse std::cout << std::left << std::setw(10) << "Index" << std::setw(15) << "User ID" << std::setw(25) << "Username" + << std::setw(25) << "Full Name" << std::setw(25) << "User Type" << std::endl; for (int iterator = 0; iterator < activeUserCount; iterator++) @@ -865,6 +866,7 @@ inline void displayAllActiveUsers(util::Map& activeUse std::cout << std::left << std::setw(10) << (iterator + 1) << std::setw(15) << user->getId() << std::setw(25) << user->getUserName() + << std::setw(25) << user->getName() << std::setw(25) << util::getUserTypeString(user->getUserType()) << std::endl; } From b7bc1f574d6f588607d994a855dd7f164fa67a68 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 27 May 2026 17:30:38 +0530 Subject: [PATCH 4/4] Implement Review Fixes --- .../services/ServiceManagementService.cpp | 6 +----- .../services/UserManagementService.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index da45370..7d907a6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -600,7 +600,6 @@ Return type: void */ void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID) { - const int INCREMENT_VALUE = 1; auto& users = m_dataStore.getUsers(); int userIndex = users.find(customerID); if (userIndex == -1) @@ -621,7 +620,6 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string& { continue; } - std::string bookingID = booking->getId(); if (booking->getCustomerId() != customerID) { continue; @@ -634,7 +632,7 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string& std::string titleToTechnician = "Customer Service Cancelled"; std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked."; std::string jobTitle = "Job Cancelled"; - std::string jobMessage = "The Job has cancelled. Your job card has been cancelled and the inventory has been restocked."; + std::string jobMessage = "The job has been cancelled. Your job card has been cancelled and the inventory has been restocked."; processBookingCancellation(booking, util::ServiceJobStatus::CANCELLED, titleToTechnician, messageToTechnician, assignedTechnician, @@ -653,7 +651,6 @@ Return type: void */ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID) { - const int INCREMENT_VALUE = 1; auto& users = m_dataStore.getUsers(); int userIndex = users.find(technicianID); if (userIndex == -1) @@ -679,7 +676,6 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia { continue; } - std::string bookingID = booking->getId(); if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED) { continue; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index df11b15..8ba68d0 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -304,16 +304,16 @@ void UserManagementService::removeUser(const std::string& userID) if (index != -1) { User* user = m_dataStore.getUsers().getValueAt(index); - if (user->getUserType() == util::UserType::CUSTOMER) - { - serviceManagementService.cancelCustomerServiceBookings(userID); - } - if (user->getUserType() == util::UserType::TECHNICIAN) - { - serviceManagementService.cancelTechnicianJobs(userID); - } if (user != nullptr) { + if (user->getUserType() == util::UserType::CUSTOMER) + { + serviceManagementService.cancelCustomerServiceBookings(userID); + } + if (user->getUserType() == util::UserType::TECHNICIAN) + { + serviceManagementService.cancelTechnicianJobs(userID); + } user->setState(util::State::INACTIVE); inventoryManagementService.detach(user); paymentManagementService.detach(user);