From a9c8ec93b7521e0339ecee5a9a5960bbdb243df0 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 27 May 2026 11:10:59 +0530 Subject: [PATCH 1/4] Fix: detach removed users from service observers - detach users from InventoryManagementService observers during removal - detach users from PaymentManagementService observers during removal - detach users from ServiceManagementService observers during removal Fixes #1783 --- .../services/UserManagementService.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index e086def..84fa7aa 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -297,6 +297,9 @@ Return type: void */ void UserManagementService::removeUser(const std::string& userID) { + InventoryManagementService inventoryManagementService; + PaymentManagementService paymentManagementService; + ServiceManagementService serviceManagementService; int index = m_dataStore.getUsers().find(userID); if (index != -1) { @@ -304,6 +307,9 @@ void UserManagementService::removeUser(const std::string& userID) if (user != nullptr) { user->setState(util::State::INACTIVE); + inventoryManagementService.detach(user); + paymentManagementService.detach(user); + serviceManagementService.detach(user); } } } From 83e2bed43268d4cade43f8d80f7cef28ebe02ccf Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 27 May 2026 12:19:34 +0530 Subject: [PATCH 2/4] Fix: improve notification display formatting and add global exception handling - add truncateString utility for long notification titles - improve notification table alignment - remove unnecessary heading spacing in notification preferences - add application-level exception handling in UserInterface::run() Fixes #1777 --- .../utilities/OutputHelper.h | 24 +++++++++ .../views/MenuHelper.h | 10 ++-- .../views/UserInterface.cpp | 51 ++++++++++++------- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/OutputHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/OutputHelper.h index 12b61a3..e763ffd 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/OutputHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/OutputHelper.h @@ -21,4 +21,28 @@ namespace util { std::cout << "\x1B[2J\x1B[H" << std::flush; } + + /* + Function: truncateString + Description: + Truncates a string if its length exceeds the given maximum length. + The truncated string ends with "..." to indicate omitted characters. + Parameters: + - text: const std::string&, input string to truncate + - maxLength: size_t, maximum allowed length of the returned string + Returns: + - std::string: Original string if within limit, otherwise truncated string with "..." + */ + inline std::string truncateString(const std::string& text, size_t maxLength) + { + if (text.length() <= maxLength) + { + return text; + } + if (maxLength <= 3) + { + return std::string(maxLength, '.'); + } + return text.substr(0, maxLength - 3) + "..."; + } } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 186c730..3d8e591 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -553,9 +553,9 @@ inline const Notification* selectNotification(const util::Vector indexedNotifications; std::cout << std::left - << std::setw(6) << "Index" + << std::setw(10) << "Index" << std::setw(15) << "ID" - << std::setw(30) << "Title" + << std::setw(35) << "Title" << std::setw(25) << "Timestamp" << std::endl; int currentIndex = 1; @@ -565,9 +565,9 @@ inline const Notification* selectNotification(const util::VectorgetId() - << std::setw(35) << currentNotification->getTitle() + << std::setw(35) << util::truncateString(currentNotification->getTitle(), 30) << std::setw(25) << currentNotification->getCreatedAt().toString() << std::endl; indexedNotifications.insert(currentIndex, currentNotification); @@ -886,7 +886,7 @@ inline bool getNotificationPreference(const std::string& serviceName) while (true) { util::clear(); - std::cout << " Configure Notification Preferences\n"; + std::cout << "Configure Notification Preferences\n"; std::cout << "\n" << serviceName << " Notifications\n"; std::cout << "1. Enable Notifications\n"; std::cout << "2. Disable Notifications\n"; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp index 3cb85ef..841e2ea 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp @@ -7,6 +7,8 @@ Author: Trenser Date:19-May-2026 */ +#include +#include #include "Enums.h" #include "InputHelper.h" #include "OutputHelper.h" @@ -23,29 +25,44 @@ Return type: void */ void UserInterface::run() { - m_controller.loadSystemData(); - m_controller.runSystemChecks(); - bool isMenuActive = true; - while (isMenuActive) + try { - try + m_controller.loadSystemData(); + m_controller.runSystemChecks(); + bool isMenuActive = true; + while (isMenuActive) { - int choice; - util::clear(); - std::cout << "Vehicle Service System\n1. Login\n2. Register Customer\n3. Exit\nEnter your Choice: "; - util::read(choice); - if (!handleOperation(choice)) + try { - isMenuActive = false; + int choice; + util::clear(); + std::cout << "Vehicle Service System\n1. Login\n2. Register Customer\n3. Exit\nEnter your Choice: "; + util::read(choice); + if (!handleOperation(choice)) + { + isMenuActive = false; + } + } + catch (const std::exception& e) + { + std::cout << "Exception: " << e.what() << std::endl; + util::pressEnter(); } } - catch (const std::exception& e) - { - std::cout << "Exception: " << e.what() << std::endl; - util::pressEnter(); - } + m_controller.saveSystemData(); + } + catch (const std::invalid_argument& exception) + { + std::cout << "Exception: Invalid Argument: " << exception.what() << std::endl; + } + catch (const std::exception& exception) + { + std::cout << "Exception: " << exception.what() << std::endl; + } + catch (...) + { + std::cout << "Unknown error occurred." << std::endl; } - m_controller.saveSystemData(); } /* From 67e5917a5706b0c461e45114bf0103a148a3d2f7 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 27 May 2026 13:02:40 +0530 Subject: [PATCH 3/4] Fix: improve complete payments table formatting and payment mode validation - Increased spacing between invoice table columns for better readability - Updated invoice table headers with clearer labels - Prevented screen clear before invoice listing display - Changed payment mode selection to re-prompt on invalid input instead of defaulting to offline mode - Removed unnecessary blank line before service listing display Fixes #1786 --- .../views/MenuHelper.h | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 3d8e591..a4cfe33 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -327,39 +327,38 @@ Returns: */ inline std::string selectInvoiceFromUserForPayment(const util::Map& currentInvoices) { - util::clear(); int currentIndex = 1, choice; util::Map pendingInvoicesForPayment; - std::cout << std::left - << std::setw(6) << "Index" - << std::setw(12) << "BookingID" - << std::setw(15) << "VehicleBrand" - << std::setw(15) << "VehicleNumber" - << std::setw(12) << "Technician ID" - << std::setw(20) << "Technician Name" - << std::setw(10) << "Discount(%)" - << std::setw(12) << "TotalAmount" - << std::setw(20) << "InvoiceDate" - << std::endl; + std::cout << std::left + << std::setw(8) << "Index" + << std::setw(15) << "Booking ID" + << std::setw(20) << "Vehicle Brand" + << std::setw(20) << "Vehicle Number" + << std::setw(18) << "Technician ID" + << std::setw(25) << "Technician Name" + << std::setw(15) << "Discount(%)" + << std::setw(15) << "TotalAmount" + << std::setw(22) << "Invoice Timestamp" + << std::endl; for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++) { const Invoice* currentInvoice = currentInvoices.getValueAt(iterator); if (currentInvoice && currentInvoice->getStatus() == util::PaymentStatus::PENDING) { const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician(); - std::cout << std::left - << std::setw(6) << currentIndex - << std::setw(12) << currentInvoice->getBookingId() - << std::setw(15) << currentInvoice->getBooking()->getVehicleBrand() - << std::setw(15) << currentInvoice->getBooking()->getVehicleNumber() - << std::setw(12) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ? - currentTechnician->getId() : "Null") - << std::setw(20) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ? - currentTechnician->getName() : "Null") - << std::setw(10) << currentInvoice->getDiscountPercentage() - << std::setw(12) << currentInvoice->getTotalAmount() - << std::setw(20) << currentInvoice->getInvoiceDate().toString() - << std::endl; + std::cout << std::left + << std::setw(8) << currentIndex + << std::setw(15) << currentInvoice->getBookingId() + << std::setw(20) << currentInvoice->getBooking()->getVehicleBrand() + << std::setw(20) << currentInvoice->getBooking()->getVehicleNumber() + << std::setw(18) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ? + currentTechnician->getId() : "Null") + << std::setw(25) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ? + currentTechnician->getName() : "Null") + << std::setw(15) << currentInvoice->getDiscountPercentage() + << std::setw(15) << currentInvoice->getTotalAmount() + << std::setw(22) << currentInvoice->getInvoiceDate().toString() + << std::endl; pendingInvoicesForPayment.insert(currentIndex++, currentInvoice); } } @@ -393,23 +392,27 @@ Returns: */ inline util::PaymentMode selectPaymentMode() { - int choice; - std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: "; - util::read(choice); - if (choice == 1) + int choice; + while (true) { - std::cout << "Offline mode selected.\n"; - return util::PaymentMode::OFFLINE; - } - else if (choice == 2) + util::clear(); + std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: "; + util::read(choice); + if (choice == 1) { - std::cout << "Online mode selected.\n"; - return util::PaymentMode::ONLINE; + std::cout << "Offline mode selected.\n"; + return util::PaymentMode::OFFLINE; + } + else if (choice == 2) + { + std::cout << "Online mode selected.\n"; + return util::PaymentMode::ONLINE; + } + else + { + std::cout << "Invalid choice. Try again.\n"; + util::pressEnter(); } - else - { - std::cout << "Invalid choice. Offline mode selected.\n"; - return util::PaymentMode::OFFLINE; } } @@ -780,7 +783,6 @@ inline const Service* selectServiceFromServices(const util::Map activeServicesMap; int currentIndex = 1; int userInputIndex; - std::cout << std::endl; std::cout << std::left << std::setw(10) << "Index" << std::setw(15) << "Service ID" From defee9aa155cc3fbc336971a7a7613ecac8a5781 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 27 May 2026 15:59:45 +0530 Subject: [PATCH 4/4] Fix ServiceBooking ID prefix and notification messages - change ServiceBooking ID prefix from `SRV` to `SBK` - add customer notification when technician is assigned to booking - fix typo in job completion exception message - remove unnecessary newline characters from completion notifications Fixes #1780 --- .../models/ServiceBooking.cpp | 5 +++-- .../services/ServiceManagementService.cpp | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp index 8e8b8d7..31b9d87 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp @@ -24,9 +24,10 @@ Parameters: None Returns: A new ServiceBooking object. */ ServiceBooking::ServiceBooking() - : m_id("SRV" + std::to_string(++m_uid)), + : m_id("SBK" + std::to_string(++m_uid)), m_customer(nullptr), m_assignedTechnician(nullptr), + m_status(util::ServiceJobStatus::PENDING), m_discountPercentage(0.0) {} /* @@ -56,7 +57,7 @@ ServiceBooking::ServiceBooking( const std::string& vehicleModel, double discountPercentage ) - : m_id("SRV" + std::to_string(++m_uid)), + : m_id("SBK" + std::to_string(++m_uid)), m_status(status), m_services(services), m_customerId(customerId), diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 2135b5e..c0478fc 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -833,6 +833,9 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const { throw std::runtime_error("Failed to create job card."); } + title = "Technician assigned"; + message = "A technician has been assigned to your Service Booking with ID " + bookingID; + sendNotification(currentBooking->getCustomer(), title, message); } /* @@ -1055,11 +1058,11 @@ void ServiceManagementService::completeJob(const std::string& jobID) } else { - throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); + throw std::runtime_error("Failed to complete the job, some error occurred or job already completed."); } if (!jobStatusUpdated) { - throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); + throw std::runtime_error("Failed to complete the job, some error occurred or job already completed."); } serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); @@ -1067,8 +1070,8 @@ void ServiceManagementService::completeJob(const std::string& jobID) { currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED); paymentManagementService.generateInvoice(currentJob->getBooking()); - std::string title = "Service Booking completed,Invoice Generated.\n"; - std::string message = "Services completed for the booking and invoice generated.\n"; + std::string title = "Service Booking completed,Invoice Generated."; + std::string message = "Services completed for the booking and invoice generated."; sendNotification(currentJob->getBooking()->getCustomer(), title, message); } } \ No newline at end of file