From 3b1f3301d61829db232472403c5c70a0aac4674f Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Fri, 12 Jun 2026 00:59:10 +0530 Subject: [PATCH 01/39] Implemented datastore shared memory codebase Changes: - Added SharedMemory module for file-backed memory-mapped storage - Added MappingInfo, FileHeader, RecordState and TrackedRecord infrastructure - Replaced CSV-based serialization with binary struct serialization - Added DataStore initialization and shutdown lifecycle management - Added datastore mutex synchronization for multi-process access - Added shared-memory mapping configuration for all datastore entities - Added generic loadRecords and saveRecords template infrastructure - Added automatic datastore directory creation and .dat file storage - Updated configuration to use binary datastore files and initial capacities - Added enum underlying types for serialization compatibility - Added password masking support for login, registration and password change flows - Added Visual Studio project configuration for shared-memory components - Added datastore-owned record caches for runtime object management - Updated datastore APIs to return cached tracked-record collections by reference - Added generic cache refresh and cleanup infrastructure - Updated save operations to persist datastore caches directly - Added automatic cache cleanup during datastore destruction - Prepared datastore for multi-process shared-memory persistence --- .../Trenser.VehicleServiceSystem.vcxproj | 11 +- ...enser.VehicleServiceSystem.vcxproj.filters | 27 + .../controllers/Controller.cpp | 1 + .../datastores/DataStore.cpp | 540 ++++++++++++++++-- .../datastores/DataStore.h | 236 +++++++- .../datastores/sharedmemory/FileHeader.h | 17 + .../datastores/sharedmemory/MappingInfo.h | 29 + .../datastores/sharedmemory/RecordState.h | 17 + .../sharedmemory/SerializedRecords.h | 109 ++++ .../datastores/sharedmemory/SharedMemory.cpp | 385 +++++++++++++ .../datastores/sharedmemory/SharedMemory.h | 29 + .../datastores/sharedmemory/TrackedRecord.h | 33 ++ .../services/UserManagementService.h | 1 + .../utilities/Config.h | 24 +- .../utilities/Enums.h | 10 +- .../utilities/FileHelper.h | 2 +- .../utilities/InputHelper.h | 43 ++ .../utilities/Utility.h | 76 +++ .../views/CustomerMenu.cpp | 1 - .../views/MenuHelper.h | 4 +- .../views/TechnicianMenu.h | 2 +- .../views/UserInterface.cpp | 6 +- 22 files changed, 1502 insertions(+), 101 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/FileHeader.h create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/MappingInfo.h create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/RecordState.h create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SerializedRecords.h create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.h create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/TrackedRecord.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 8627061..6a269f5 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -102,7 +102,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;%(AdditionalIncludeDirectories) + $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;$(ProjectDir)datastores\sharedmemory;%(AdditionalIncludeDirectories) Console @@ -117,7 +117,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;%(AdditionalIncludeDirectories) + $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;$(ProjectDir)datastores\sharedmemory;%(AdditionalIncludeDirectories) Console @@ -129,6 +129,7 @@ + @@ -156,6 +157,12 @@ + + + + + + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index 73b9291..7e74b19 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -64,6 +64,12 @@ {8057b93d-51a9-42df-b06e-01ce395f6308} + + {ec639004-44c6-4bd6-9963-077adde82b5f} + + + {7aa8722e-adfa-466e-8211-de63f3b7892b} + @@ -141,6 +147,9 @@ Source Files\Models + + Source Files\DataStores\SharedMemory + @@ -251,5 +260,23 @@ Header Files\Views + + Header Files\DataStores\SharedMemory + + + Header Files\DataStores\SharedMemory + + + Header Files\DataStores\SharedMemory + + + Header Files\DataStores\SharedMemory + + + Header Files\DataStores\SharedMemory + + + Header Files\DataStores\SharedMemory + \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp index e3a85b0..9bc1d86 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp @@ -6,6 +6,7 @@ Description: Implementation file containing the method definitions of the Author: Trenser Date:19-May-2026 */ + #include "ComboPackage.h" #include "Controller.h" #include "Enums.h" diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 3419f44..1cade32 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -9,6 +9,200 @@ Date: 19-May-2026 */ #include "DataStore.h" +#include "Config.h" +#include "SerializedRecords.h" +#include "FileHelper.h" + +/* +Function: DataStore +Description: Constructs the DataStore singleton and initializes + internal handles to their default values. +Parameters: + - None +Returns: + - None +*/ +DataStore::DataStore() : + m_globalMutex(NULL) {} + +/* +Function: ~DataStore +Description: Destroys the DataStore singleton and releases all + cached application objects owned by the datastore. + This includes users, notifications, services, + combo packages, inventory items, service bookings, + job cards, and invoices. +Parameters: + - None +Returns: + - None +*/ +DataStore::~DataStore() +{ + clearCache(m_userCache); + clearCache(m_notificationCache); + clearCache(m_serviceCache); + clearCache(m_comboPackageCache); + clearCache(m_inventoryItemCache); + clearCache(m_serviceBookingCache); + clearCache(m_jobCardCache); + clearCache(m_invoiceCache); +} + +/* +Function: initialize +Description: Initializes the shared-memory datastore. + Creates or opens the global datastore mutex, + configures all mapping metadata, and creates + or opens the file mappings used to persist + application data. After successful completion, + all datastore files are mapped into memory and + ready for use by the application. +Parameter: None +Return type: bool + - true : Initialization completed successfully. + - false : Failed to create the mutex or open + one or more file mappings. +*/ +bool DataStore::initialize() +{ + m_globalMutex = CreateMutexA(NULL, FALSE, "VehicleServiceSystemMutex"); + if (m_globalMutex == NULL) + { + return false; + } + if (!lockDataStore()) + { + CloseHandle(m_globalMutex); + m_globalMutex = NULL; + return false; + } + bool success = true; + do + { + util::ensureDirectoryExists(config::file::DIRECTORY); + m_users.fileName = config::file::USER_FILE; + m_users.recordSize = sizeof(SerializedUser); + m_notifications.fileName = config::file::NOTIFICATION_FILE; + m_notifications.recordSize = sizeof(SerializedNotification); + m_services.fileName = config::file::SERVICE_FILE; + m_services.recordSize = sizeof(SerializedService); + m_comboPackages.fileName = config::file::COMBOPACKAGE_FILE; + m_comboPackages.recordSize = sizeof(SerializedComboPackage); + m_inventoryItems.fileName = config::file::INVENTORYITEM_FILE; + m_inventoryItems.recordSize = sizeof(SerializedInventoryItem); + m_serviceBookings.fileName = config::file::SERVICEBOOKING_FILE; + m_serviceBookings.recordSize = sizeof(SerializedServiceBooking); + m_jobCards.fileName = config::file::JOBCARD_FILE; + m_jobCards.recordSize = sizeof(SerializedJobCard); + m_invoices.fileName = config::file::INVOICE_FILE; + m_invoices.recordSize = sizeof(SerializedInvoice); + m_serviceManagementObservers.fileName = config::file::SERVICEMANAGEMENTOBSERVERS; + m_serviceManagementObservers.recordSize = sizeof(SerializedObserver); + m_paymentManagementObservers.fileName = config::file::PAYMENTMANAGEMENTOBSERVERS; + m_paymentManagementObservers.recordSize = sizeof(SerializedObserver); + m_inventoryManagementObservers.fileName = config::file::INVENTORYMANAGEMENTOBSERVERS; + m_inventoryManagementObservers.recordSize = sizeof(SerializedObserver); + if (!SharedMemory::createOrOpenMapping(m_users)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_notifications)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_services)) + { + success = false; + break; + } + + if (!SharedMemory::createOrOpenMapping(m_comboPackages)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_inventoryItems)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_serviceBookings)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_jobCards)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_invoices)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_payments)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_serviceManagementObservers)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_paymentManagementObservers)) + { + success = false; + break; + } + if (!SharedMemory::createOrOpenMapping(m_inventoryManagementObservers)) + { + success = false; + break; + } + } while (false); + unlockDataStore(); + if (!success) + { + shutdown(); + } + return success; +} + +/* +Function: shutdown +Description: Releases all shared-memory resources owned by the + datastore. Closes every file mapping, unmaps all + mapped views, and releases the datastore mutex. + After this call, the datastore must be initialized + again before use. +Parameter: None +Return type: void +*/ +void DataStore::shutdown() +{ + SharedMemory::closeMapping(m_users); + SharedMemory::closeMapping(m_notifications); + SharedMemory::closeMapping(m_services); + SharedMemory::closeMapping(m_comboPackages); + SharedMemory::closeMapping(m_inventoryItems); + SharedMemory::closeMapping(m_serviceBookings); + SharedMemory::closeMapping(m_jobCards); + SharedMemory::closeMapping(m_invoices); + SharedMemory::closeMapping(m_payments); + SharedMemory::closeMapping(m_serviceManagementObservers); + SharedMemory::closeMapping(m_paymentManagementObservers); + SharedMemory::closeMapping(m_inventoryManagementObservers); + if (m_globalMutex != NULL) + { + CloseHandle(m_globalMutex); + m_globalMutex = NULL; + } +} /* Function: getInstance @@ -26,104 +220,346 @@ DataStore& DataStore::getInstance() /* Function: getUsers -Description: Retrieves the internal map of users. +Description: Retrieves all user records from the datastore. Parameters: - None Returns: - - Reference to util::Map containing all users. + - util::Map>: Collection of user records */ -util::Map& DataStore::getUsers() +util::Map>& DataStore::getUsers() { - return m_users; + return m_userCache; +} + +/* +Function: getNotifications +Description: Retrieves all notification records from the datastore. +Parameters: + - None +Returns: + - util::Map>: Collection of notification records +*/ +util::Map>& DataStore::getNotifications() +{ + return m_notificationCache; } /* Function: getServices -Description: Retrieves the internal map of services. +Description: Retrieves all service records from the datastore. Parameters: - None Returns: - - Reference to util::Map containing all services. + - util::Map>: Collection of service records */ -util::Map& DataStore::getServices() +util::Map>& DataStore::getServices() { - return m_services; + return m_serviceCache; } /* Function: getComboPackages -Description: Retrieves the internal map of combo packages. +Description: Retrieves all combo package records from the datastore. Parameters: - None Returns: - - Reference to util::Map containing all combo packages. + - util::Map>: Collection of combo package records */ -util::Map& DataStore::getComboPackages() +util::Map>& DataStore::getComboPackages() { - return m_comboPackages; -} - -/* -Function: getServiceBookings -Description: Retrieves the internal map of service bookings. -Parameters: - - None -Returns: - - Reference to util::Map containing all service bookings. -*/ -util::Map& DataStore::getServiceBookings() -{ - return m_serviceBookings; -} - -/* -Function: getJobCards -Description: Retrieves the internal map of job cards. -Parameters: - - None -Returns: - - Reference to util::Map containing all job cards. -*/ -util::Map& DataStore::getJobCards() -{ - return m_jobCards; + return m_comboPackageCache; } /* Function: getInventoryItems -Description: Retrieves the internal map of inventory items. +Description: Retrieves all inventory item records from the datastore. Parameters: - None Returns: - - Reference to util::Map containing all inventory items. + - util::Map>: Collection of inventory item records */ -util::Map& DataStore::getInventoryItems() +util::Map>& DataStore::getInventoryItems() { - return m_inventoryItems; + return m_inventoryItemCache; +} + +/* +Function: getServiceBookings +Description: Retrieves all service booking records from the datastore. +Parameters: + - None +Returns: + - util::Map>: Collection of service booking records +*/ +util::Map>& DataStore::getServiceBookings() +{ + return m_serviceBookingCache; +} + +/* +Function: getJobCards +Description: Retrieves all job card records from the datastore. +Parameters: + - None +Returns: + - util::Map>: Collection of job card records +*/ +util::Map>& DataStore::getJobCards() +{ + return m_jobCardCache; } /* Function: getInvoices -Description: Retrieves the internal map of invoices. +Description: Retrieves all invoice records from the datastore. Parameters: - None Returns: - - Reference to util::Map containing all invoices. + - util::Map>: Collection of invoice records */ -util::Map& DataStore::getInvoices() +util::Map>& DataStore::getInvoices() { - return m_invoices; + return m_invoiceCache; } /* -Function: getPayments -Description: Retrieves the internal map of payments. +Function: getServiceManagementObservers +Description: Retrieves all service management observer records from the datastore. Parameters: - None Returns: - - Reference to util::Map containing all payments. + - util::Map: Collection of observer records */ -util::Map& DataStore::getPayments() +util::Map DataStore::getServiceManagementObservers() { - return m_payments; -} \ No newline at end of file + return util::Map(); +} + +/* +Function: getPaymentManagementObservers +Description: Retrieves all payment management observer records from the datastore. +Parameters: + - None +Returns: + - util::Map: Collection of observer records +*/ +util::Map DataStore::getPaymentManagementObservers() +{ + return util::Map(); +} + +/* +Function: getInventoryManagementObservers +Description: Retrieves all inventory management observer records from the datastore. +Parameters: + - None +Returns: + - util::Map: Collection of observer records +*/ +util::Map DataStore::getInventoryManagementObservers() +{ + return util::Map(); +} + +/* +Function: saveUsers +Description: Persists all user records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveUsers() +{ +} + +/* +Function: saveNotifications +Description: Persists all notification records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveNotifications() +{ +} + +/* +Function: saveServices +Description: Persists all service records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveServices() +{ +} + +/* +Function: saveComboPackages +Description: Persists all combo package records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveComboPackages() +{ +} + +/* +Function: saveInventoryItems +Description: Persists all inventory item records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveInventoryItems() +{ +} + +/* +Function: saveServiceBookings +Description: Persists all service booking records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveServiceBookings() +{ +} + +/* +Function: saveJobCards +Description: Persists all job card records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveJobCards() +{ +} + +/* +Function: saveInvoices +Description: Persists all invoice records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveInvoices() +{ +} + +/* +Function: saveServiceManagementObservers +Description: Persists all service management observer records to the datastore. +Parameters: + - observers: util::Map>&, collection of observer records +Returns: + - None +*/ +void DataStore::saveServiceManagementObservers(util::Map& observers) +{ +} + +/* +Function: savePaymentManagementObservers +Description: Persists all payment management observer records to the datastore. +Parameters: + - observers: util::Map&, collection of observer records +Returns: + - None +*/ +void DataStore::savePaymentManagementObservers(util::Map& observers) +{ +} + +/* +Function: saveInventoryManagementObservers +Description: Persists all inventory management observer records to the datastore. +Parameters: + - observers: util::Map&, collection of observer records +Returns: + - None +*/ +void DataStore::saveInventoryManagementObservers(util::Map& observers) +{ +} + +/* +Function: lockDataStore +Description: Acquires exclusive access to the datastore. +Parameters: + - None +Returns: + - bool: True if the datastore was successfully locked, otherwise false +*/ +bool DataStore::lockDataStore() +{ + return false; +} + +/* +Function: unlockDataStore +Description: Releases exclusive access to the datastore. +Parameters: + - None +Returns: + - bool: True if the datastore was successfully unlocked, otherwise false +*/ +bool DataStore::unlockDataStore() +{ + return false; +} + +/* +Function: lockDataStore +Description: Acquires the datastore mutex, providing + exclusive access to the shared-memory + datastore. This function blocks until + the mutex becomes available or an error + occurs. +Parameter: None +Return type: + bool + - true : Mutex successfully acquired. + - false : Failed to acquire the mutex. +*/ +bool DataStore::lockDataStore() +{ + if (m_globalMutex == NULL) + { + return false; + } + DWORD result = WaitForSingleObject(m_globalMutex, INFINITE); + return result == WAIT_OBJECT_0; +} + +/* +Function: unlockDataStore +Description: Releases the datastore mutex after a + successful call to lockDataStore(), + allowing other processes to access the + shared-memory datastore. +Parameter: None +Return type: + bool + - true : Mutex successfully released. + - false : Failed to release the mutex. +*/ +bool DataStore::unlockDataStore() +{ + if (m_globalMutex == NULL) + { + return false; + } + return ReleaseMutex(m_globalMutex) != 0; +} + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index cde9b4e..3950b11 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -6,42 +6,232 @@ Date: 19-May-2026 */ #pragma once +#include #include #include "Map.h" - +#include "MappingInfo.h" +#include "TrackedRecord.h" +#include "SharedMemory.h" class User; +class Notification; class Service; class ComboPackage; +class InventoryItem; class ServiceBooking; class JobCard; -class InventoryItem; class Invoice; -class Payment; class DataStore { private: - util::Map m_users; - util::Map m_services; - util::Map m_comboPackages; - util::Map m_serviceBookings; - util::Map m_jobCards; - util::Map m_inventoryItems; - util::Map m_invoices; - util::Map m_payments; - DataStore() {} -public: - static DataStore& getInstance(); + DataStore(); + ~DataStore(); DataStore(const DataStore&) = delete; DataStore& operator=(const DataStore&) = delete; DataStore(DataStore&&) = delete; DataStore& operator=(DataStore&&) = delete; - util::Map& getUsers(); - util::Map& getServices(); - util::Map& getComboPackages(); - util::Map& getServiceBookings(); - util::Map& getJobCards(); - util::Map& getInventoryItems(); - util::Map& getInvoices(); - util::Map& getPayments(); -}; \ No newline at end of file + HANDLE m_globalMutex; + MappingInfo m_users; + MappingInfo m_notifications; + MappingInfo m_services; + MappingInfo m_comboPackages; + MappingInfo m_inventoryItems; + MappingInfo m_serviceBookings; + MappingInfo m_jobCards; + MappingInfo m_invoices; + MappingInfo m_payments; + MappingInfo m_serviceManagementObservers; + MappingInfo m_paymentManagementObservers; + MappingInfo m_inventoryManagementObservers; + util::Map> m_userCache; + util::Map> m_notificationCache; + util::Map> m_serviceCache; + util::Map> m_comboPackageCache; + util::Map> m_inventoryItemCache; + util::Map> m_serviceBookingCache; + util::Map> m_jobCardCache; + util::Map> m_invoiceCache; +public: + static DataStore& getInstance(); + bool initialize(); + void shutdown(); + util::Map>& getUsers(); + util::Map>& getNotifications(); + util::Map>& getServices(); + util::Map>& getComboPackages(); + util::Map>& getInventoryItems(); + util::Map>& getServiceBookings(); + util::Map>& getJobCards(); + util::Map>& getInvoices(); + util::Map getServiceManagementObservers(); + util::Map getPaymentManagementObservers(); + util::Map getInventoryManagementObservers(); + void saveUsers(); + void saveNotifications(); + void saveServices(); + void saveComboPackages(); + void saveInventoryItems(); + void saveServiceBookings(); + void saveJobCards(); + void saveInvoices(); + void saveServiceManagementObservers(util::Map& observers); + void savePaymentManagementObservers(util::Map& observers); + void saveInventoryManagementObservers(util::Map& observers); + bool lockDataStore(); + bool unlockDataStore(); +private: + template + util::Map> loadRecords(MappingInfo& mapping); + template + void saveRecords(MappingInfo& mapping, util::Map>& records); + template void clearCache(util::Map>&cache); + template void refreshCache(util::Map>&cache, util::Map>&refreshedCache); +}; + +/* +Function: loadRecords +Description: Loads all serialized records from the specified + shared-memory mapping, deserializes them into + application objects, and returns them as a map + of tracked records. Each loaded record is marked + as CLEAN and assigned its corresponding slot index + within the mapping so that future modifications + can be written back efficiently. +Parameter: + - mapping: Reference to the mapping containing the + serialized records. +Return type: + util::Map> + A map containing all loaded records keyed by + their unique identifier. +*/ +template +util::Map> DataStore::loadRecords(MappingInfo& mapping) +{ + util::Map> records; + SharedMemory::ensureLatestMapping(mapping); + size_t recordCount = SharedMemory::getRecordCount(mapping); + for (size_t index = 0; index < recordCount; ++index) + { + TSerialized* serialized = static_cast(SharedMemory::getRecordAddress(mapping,index)); + TObject* object = TObject::deserialize(*serialized); + TrackedRecord trackedRecord; + trackedRecord.data = object; + trackedRecord.state = RecordState::CLEAN; + trackedRecord.slotIndex = index; + records.insert(object->getId(), trackedRecord); + } + return records; +} + +/* +Function: saveRecords +Description: Persists all modified and newly added records + contained in the provided map to the specified + shared-memory mapping. Modified records overwrite + their existing slots, while new records are + appended to the end of the mapping. Records marked + as CLEAN are ignored. +Parameter: + - mapping: Reference to the mapping where records are + stored. + - records: Map containing the records to be persisted. +Return type: void +*/ +template void DataStore::saveRecords(MappingInfo& mapping, util::Map>& records) +{ + SharedMemory::ensureLatestMapping(mapping); + for (int index = 0; index < records.getSize(); ++index) + { + TrackedRecord& record = records.getValueAt(index); + if (record.state == RecordState::CLEAN) + { + continue; + } + TSerialized serialized = record.data->serialize(); + if (record.state == RecordState::MODIFIED) + { + TSerialized* destination = static_cast(SharedMemory::getRecordAddress(mapping, record.slotIndex)); + if (destination) + { + *destination = serialized; + } + } + else if (record.state == RecordState::NEW_RECORD) + { + if (!SharedMemory::ensureCapacityForInsert(mapping)) + { + continue; + } + size_t recordCount = SharedMemory::getRecordCount(mapping); + TSerialized* destination = static_cast(SharedMemory::getRecordAddress(mapping,recordCount)); + *destination = serialized; + SharedMemory::setRecordCount(mapping, recordCount + 1); + record.slotIndex = recordCount; + } + record.state = RecordState::CLEAN; + } +} + +/* +Function: clearCache +Description: Releases all objects owned by the cache and + clears the cache contents. +Parameters: + - cache: Cache to be cleared. +Returns: + - None +*/ +template +void DataStore::clearCache(util::Map>&cache) +{ + for (int index = 0; index < cache.getSize(); ++index) + { + delete cache.getValueAt(index).data; + cache.getValueAt(index).data = nullptr; + } + cache.clear(); +} + +/* +Function: refreshCache +Description: Refreshes the cache while preserving object addresses + for records that already exist. Existing objects are + updated in-place so that pointers held elsewhere remain + valid after the refresh. +Parameters: + - cache: Existing cache to refresh. + - refreshedCache: Newly loaded cache contents. +Returns: + - None +*/ +template +void DataStore::refreshCache(util::Map>& cache, util::Map>& refreshedCache) +{ + util::Map> oldCache = cache; + cache.clear(); + for (int index = 0; index < refreshedCache.getSize(); ++index) + { + const std::string& id = refreshedCache.getKeyAt(index); + TrackedRecord& refreshedRecord = refreshedCache.getValueAt(index); + int oldIndex = oldCache.find(id); + if (oldIndex != -1) + { + TrackedRecord& oldRecord = oldCache.getValueAt(oldIndex); + *oldRecord.data = *refreshedRecord.data; + oldRecord.slotIndex = refreshedRecord.slotIndex; + oldRecord.state = refreshedRecord.state; + delete refreshedRecord.data; + refreshedRecord.data = oldRecord.data; + } + cache.insert(id, refreshedRecord); + } + for (int index = 0; index < oldCache.getSize(); ++index) + { + const std::string& id = oldCache.getKeyAt(index); + if (cache.find(id) == -1) + { + delete oldCache.getValueAt(index).data; + } + } +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/FileHeader.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/FileHeader.h new file mode 100644 index 0000000..929dbf4 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/FileHeader.h @@ -0,0 +1,17 @@ +/* +File: FileHeader.h +Description: Defines the FileHeader structure used to store + metadata for binary record files, including + record count and capacity. +Author: Trenser +Created: 10-June-2026 +*/ + +#pragma once +#include + +struct FileHeader +{ + size_t recordCount; + size_t capacity; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/MappingInfo.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/MappingInfo.h new file mode 100644 index 0000000..ebd5123 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/MappingInfo.h @@ -0,0 +1,29 @@ +/* +File: MappingInfo.h +Description: Defines the MappingInfo structure used for + managing Windows file mapping operations. + Stores handles, mapped view pointer, + file metadata, and capacity information. +Author: Trenser +Created: 10-June-2026 +*/ + +#pragma once +#include +#include + +struct MappingInfo +{ + HANDLE fileHandle; + HANDLE mappingHandle; + void* mappedView; + std::string fileName; + size_t recordSize; + size_t mappedCapacity; + MappingInfo() + : fileHandle(NULL), + mappingHandle(NULL), + mappedView(nullptr), + recordSize(0), + mappedCapacity(0) {} +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/RecordState.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/RecordState.h new file mode 100644 index 0000000..95402fd --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/RecordState.h @@ -0,0 +1,17 @@ +/* +File: RecordState.h +Description: Defines the RecordState enumeration used to + represent the state of a record in storage. + States include CLEAN, NEW_RECORD, and MODIFIED. +Author: Trenser +Created: 10-June-2026 +*/ + +#pragma once + +enum class RecordState : int +{ + CLEAN, + NEW_RECORD, + MODIFIED +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SerializedRecords.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SerializedRecords.h new file mode 100644 index 0000000..2d18842 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SerializedRecords.h @@ -0,0 +1,109 @@ +/* +File: SerializedRecords.h +Description: Defines serialized structures for persistent storage + and retrieval of system entities including User, + Notification, Service, ComboPackage, InventoryItem, + ServiceBooking, JobCard, Invoice, and Observer. + These structures use fixed-size character arrays + and primitive types for binary serialization. +Author: Trenser +Created: 10-June-2026 +*/ + +#pragma once +#include "Utility.h" +#include "Enums.h" +#include "Timestamp.h" + +struct SerializedUser +{ + char id[64]; + char username[64]; + char password[64]; + char name[128]; + char phone[32]; + char email[128]; + util::UserType userType; + util::State status; +}; + +struct SerializedNotification +{ + char id[64]; + char recipientUserId[64]; + char title[128]; + char message[1024]; + util::Timestamp createdAt; + util::State state; +}; + +struct SerializedService +{ + char id[64]; + char name[128]; + char inventoryItemIDs[1024]; + double laborCost; + util::State status; +}; + +struct SerializedComboPackage +{ + char id[64]; + char packageName[128]; + double discountPercentage; + char serviceIDs[1024]; + util::State status; +}; + +struct SerializedInventoryItem +{ + char id[64]; + char partName[128]; + int quantity; + double price; + util::State status; +}; + +struct SerializedServiceBooking +{ + char id[64]; + util::ServiceJobStatus status; + char serviceIDs[1024]; + char customerId[64]; + char vehicleNumber[64]; + char vehicleBrand[64]; + char vehicleModel[64]; + char assignedTechnicianId[64]; + double discountPercentage; +}; + +struct SerializedJobCard +{ + char id[64]; + char bookingId[64]; + char serviceId[64]; + char technicianId[64]; + util::Timestamp assignedDate; + util::ServiceJobStatus status; + util::Timestamp completionDate; +}; + +struct SerializedInvoice +{ + char id[64]; + char bookingId[64]; + util::Timestamp invoiceDate; + char partIDs[1024]; + double laborCost; + double partsCost; + double discountPercentage; + double totalAmount; + util::Timestamp paymentDate; + util::PaymentMode paymentMethod; + util::PaymentStatus status; +}; + +struct SerializedObserver +{ + char id[64]; +}; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp new file mode 100644 index 0000000..6a10c9f --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp @@ -0,0 +1,385 @@ +/* +File: SharedMemory.cpp +Description: Implements shared memory utilities for managing + Windows file mapping operations. Provides functions + to create, open, resize, and close mappings, as well + as access headers, records, and ensure synchronization + across processes. +Author: Trenser +Created: 11-June-2026 +*/ + +#include "SharedMemory.h" +#include "Config.h" + +/* +Function: invalidateMapping +Description: Releases all mapping resources and resets the mapping to an invalid state. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - None +*/ +static void invalidateMapping(MappingInfo& mapping) +{ + if (mapping.mappedView != nullptr) + { + UnmapViewOfFile(mapping.mappedView); + mapping.mappedView = nullptr; + } + if (mapping.mappingHandle != NULL) + { + CloseHandle(mapping.mappingHandle); + mapping.mappingHandle = NULL; + } + if (mapping.fileHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(mapping.fileHandle); + mapping.fileHandle = INVALID_HANDLE_VALUE; + } + mapping.mappedCapacity = 0; +} + +/* +Function: createOrOpenMapping +Description: Creates or opens a file mapping and maps it into the process address space. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - bool: True if the mapping was successfully created/opened, otherwise false +*/ +bool SharedMemory::createOrOpenMapping(MappingInfo& mapping) +{ + if (mapping.recordSize == 0) + { + return false; + } + mapping.fileHandle = + CreateFileA( + mapping.fileName.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (mapping.fileHandle == INVALID_HANDLE_VALUE) + { + return false; + } + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(mapping.fileHandle, &fileSize)) + { + invalidateMapping(mapping); + return false; + } + bool isNewFile = (fileSize.QuadPart == 0); + const size_t initialCapacity = config::file::INITIAL_CAPACITY; + if (isNewFile) + { + LARGE_INTEGER newSize; + newSize.QuadPart = sizeof(FileHeader) + initialCapacity * mapping.recordSize; + if (!SetFilePointerEx(mapping.fileHandle, newSize, NULL, FILE_BEGIN)) + { + invalidateMapping(mapping); + return false; + } + if (!SetEndOfFile(mapping.fileHandle)) + { + invalidateMapping(mapping); + return false; + } + } + mapping.mappingHandle = + CreateFileMappingA( + mapping.fileHandle, + NULL, + PAGE_READWRITE, + 0, + 0, + NULL); + if (mapping.mappingHandle == NULL) + { + invalidateMapping(mapping); + return false; + } + mapping.mappedView = + MapViewOfFile( + mapping.mappingHandle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + 0); + if (mapping.mappedView == nullptr) + { + invalidateMapping(mapping); + return false; + } + FileHeader* header = getHeader(mapping); + if (header == nullptr) + { + invalidateMapping(mapping); + return false; + } + if (isNewFile) + { + header->recordCount = 0; + header->capacity = initialCapacity; + } + mapping.mappedCapacity = header->capacity; + return true; +} + +/* +Function: closeMapping +Description: Unmaps and closes all resources associated with a file mapping. +Parameters: + - mapping: MappingInfo&, mapping to close +Returns: + - None +*/ +void SharedMemory::closeMapping(MappingInfo& mapping) +{ + invalidateMapping(mapping); +} + +/* +Function: getHeader +Description: Returns the file header stored at the beginning of the mapped memory region. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - FileHeader*: Pointer to the file header, or nullptr if the mapping is not valid +*/ +FileHeader* SharedMemory::getHeader(MappingInfo& mapping) +{ + if (mapping.mappedView == nullptr) + { + return nullptr; + } + return reinterpret_cast(mapping.mappedView); +} + +/* +Function: getRecordAddress +Description: Returns the address of a record at the specified index within the mapped memory region. +Parameters: + - mapping: MappingInfo&, mapping information and handles + - index: size_t, record index +Returns: + - void*: Pointer to the record, or nullptr if the mapping is not valid +*/ +void* SharedMemory::getRecordAddress(MappingInfo& mapping, size_t index) +{ + if (mapping.mappedView == nullptr) + { + return nullptr; + } + return reinterpret_cast(mapping.mappedView) + sizeof(FileHeader) + index * mapping.recordSize; +} + +/* +Function: getRecordCount +Description: Returns the number of records currently stored in the mapping. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - size_t: Number of records in the mapping +*/ +size_t SharedMemory::getRecordCount(MappingInfo& mapping) +{ + FileHeader* header = getHeader(mapping); + if (header == nullptr) + { + return 0; + } + return header->recordCount; +} + +/* +Function: setRecordCount +Description: Updates the number of records stored in the mapping. +Parameters: + - mapping: MappingInfo&, mapping information and handles + - count: size_t, new record count +Returns: + - None +*/ +void SharedMemory::setRecordCount(MappingInfo& mapping, size_t count) +{ + FileHeader* header = getHeader(mapping); + if (header == nullptr) + { + return; + } + header->recordCount = count; +} + +/* +Function: getCapacity +Description: Returns the current capacity of the mapping. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - size_t: Maximum number of records that can be stored in the mapping +*/ +size_t SharedMemory::getCapacity(MappingInfo& mapping) +{ + FileHeader* header = getHeader(mapping); + if (header == nullptr) + { + return 0; + } + return header->capacity; +} + +/* +Function: resizeMapping +Description: Resizes the file mapping to the specified capacity. +Parameters: + - mapping: MappingInfo&, mapping information and handles + - newCapacity: size_t, new mapping capacity +Returns: + - bool: True if the resize succeeded, otherwise false +*/ +bool SharedMemory::resizeMapping(MappingInfo& mapping, size_t newCapacity) +{ + FileHeader* header = getHeader(mapping); + if (header == nullptr) + { + invalidateMapping(mapping); + return false; + } + header->capacity = newCapacity; + if (!FlushViewOfFile(mapping.mappedView, sizeof(FileHeader))) + { + return false; + } + if (!UnmapViewOfFile(mapping.mappedView)) + { + return false; + } + mapping.mappedView = nullptr; + CloseHandle(mapping.mappingHandle); + mapping.mappingHandle = NULL; + LARGE_INTEGER newSize; + newSize.QuadPart = sizeof(FileHeader) + newCapacity * mapping.recordSize; + if (!SetFilePointerEx(mapping.fileHandle, newSize, NULL, FILE_BEGIN)) + { + invalidateMapping(mapping); + return false; + } + if (!SetEndOfFile(mapping.fileHandle)) + { + invalidateMapping(mapping); + return false; + } + mapping.mappingHandle = + CreateFileMappingA( + mapping.fileHandle, + NULL, + PAGE_READWRITE, + 0, + 0, + NULL); + if (mapping.mappingHandle == NULL) + { + invalidateMapping(mapping); + return false; + } + mapping.mappedView = + MapViewOfFile( + mapping.mappingHandle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + 0); + if (mapping.mappedView == nullptr) + { + invalidateMapping(mapping); + return false; + } + mapping.mappedCapacity = newCapacity; + return true; +} + +/* +Function: ensureCapacityForInsert +Description: Ensures that the mapping has space for at least one additional record, growing it if necessary. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - bool: True if capacity is available, otherwise false +*/ +bool SharedMemory::ensureCapacityForInsert(MappingInfo& mapping) +{ + size_t recordCount = getRecordCount(mapping); + size_t capacity = getCapacity(mapping); + if (recordCount < capacity) + { + return true; + } + return resizeMapping(mapping, capacity * 2); +} + +/* +Function: ensureLatestMapping +Description: Remaps the file if another process has resized it. +Parameters: + - mapping: MappingInfo&, mapping information and handles +Returns: + - bool: True if the mapping is valid and up to date, otherwise false +*/ +bool SharedMemory::ensureLatestMapping(MappingInfo& mapping) +{ + FileHeader* header = getHeader(mapping); + if (header == nullptr) + { + return false; + } + if (header->capacity == mapping.mappedCapacity) + { + return true; + } + if (!UnmapViewOfFile(mapping.mappedView)) + { + invalidateMapping(mapping); + return false; + } + mapping.mappedView = nullptr; + CloseHandle(mapping.mappingHandle); + mapping.mappingHandle = NULL; + mapping.mappingHandle = + CreateFileMappingA( + mapping.fileHandle, + NULL, + PAGE_READWRITE, + 0, + 0, + NULL); + if (mapping.mappingHandle == NULL) + { + invalidateMapping(mapping); + return false; + } + mapping.mappedView = + MapViewOfFile( + mapping.mappingHandle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + 0); + if (mapping.mappedView == nullptr) + { + invalidateMapping(mapping); + return false; + } + header = getHeader(mapping); + if (header == nullptr) + { + invalidateMapping(mapping); + return false; + } + mapping.mappedCapacity = header->capacity; + return true; +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.h new file mode 100644 index 0000000..a288ab3 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.h @@ -0,0 +1,29 @@ +/* +File: SharedMemory.h +Description: Declares functions for managing Windows file + mapping and shared memory operations. Provides + utilities for creating, resizing, and closing + mappings, as well as accessing headers and + record data. +Author: Trenser +Created: 10-June-2026 +*/ + +#pragma once +#include +#include "MappingInfo.h" +#include "FileHeader.h" + +namespace SharedMemory +{ + bool createOrOpenMapping(MappingInfo& mapping); + void closeMapping(MappingInfo& mapping); + bool ensureLatestMapping(MappingInfo& mapping); + bool resizeMapping(MappingInfo& mapping, size_t newCapacity); + FileHeader* getHeader(MappingInfo& mapping); + void* getRecordAddress(MappingInfo& mapping, size_t index); + size_t getRecordCount(MappingInfo& mapping); + void setRecordCount(MappingInfo& mapping, size_t count); + size_t getCapacity(MappingInfo& mapping); + bool ensureCapacityForInsert(MappingInfo& mapping); +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/TrackedRecord.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/TrackedRecord.h new file mode 100644 index 0000000..5ff9ac9 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/TrackedRecord.h @@ -0,0 +1,33 @@ +/* +File: TrackedRecord.h +Description: Defines the TrackedRecord template structure used + to manage objects with associated record state and + slot index. Supports tracking of CLEAN, NEW_RECORD, + and MODIFIED states for persistence and synchronization. +Author: Trenser +Created: 10-June-2026 +*/ + +#pragma once +#include "RecordState.h" + +static const size_t INVALID_SLOT = static_cast(-1); + +template +struct TrackedRecord +{ + T* data; + RecordState state; + size_t slotIndex; + TrackedRecord() + : data(nullptr), + state(RecordState::CLEAN), + slotIndex(INVALID_SLOT) {} + TrackedRecord( + T* object, + RecordState recordState, + size_t slot) + : data(object), + state(recordState), + slotIndex(slot) {} +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h index 6b9f518..b448872 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h @@ -6,6 +6,7 @@ Description: Header file declaring the UserManagementService class, which manage Author: Trenser Date:19-May-2026 */ + #pragma once #include #include "Map.h" diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h index b0bd19d..2eef19d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h @@ -28,16 +28,18 @@ namespace config namespace file { - constexpr const char* INVENTORYITEM_FILE = "files/InventoryItem.csv"; - constexpr const char* USER_FILE = "files/User.csv"; - constexpr const char* NOTIFICATION_FILE = "files/Notification.csv"; - constexpr const char* SERVICE_FILE = "files/Service.csv"; - constexpr const char* COMBOPACKAGE_FILE = "files/ComboPackage.csv"; - constexpr const char* SERVICEBOOKING_FILE = "files/ServiceBooking.csv"; - constexpr const char* JOBCARD_FILE = "files/JobCard.csv"; - constexpr const char* INVOICE_FILE = "files/Invoice.csv"; - constexpr const char* SERVICEMANAGEMENTOBSERVERS = "files/ServiceManagementObservers.csv"; - constexpr const char* PAYMENTMANAGEMENTOBSERVERS = "files/PaymentManagementObservers.csv"; - constexpr const char* INVENTORYMANAGEMENTOBSERVERS = "files/InventoryManagementObservers.csv"; + const size_t INITIAL_CAPACITY = 100; + constexpr const char* DIRECTORY = "files/"; + constexpr const char* INVENTORYITEM_FILE = "files/InventoryItem.dat"; + constexpr const char* USER_FILE = "files/User.dat"; + constexpr const char* NOTIFICATION_FILE = "files/Notification.dat"; + constexpr const char* SERVICE_FILE = "files/Service.dat"; + constexpr const char* COMBOPACKAGE_FILE = "files/ComboPackage.dat"; + constexpr const char* SERVICEBOOKING_FILE = "files/ServiceBooking.dat"; + constexpr const char* JOBCARD_FILE = "files/JobCard.dat"; + constexpr const char* INVOICE_FILE = "files/Invoice.dat"; + constexpr const char* SERVICEMANAGEMENTOBSERVERS = "files/ServiceManagementObservers.dat"; + constexpr const char* PAYMENTMANAGEMENTOBSERVERS = "files/PaymentManagementObservers.dat"; + constexpr const char* INVENTORYMANAGEMENTOBSERVERS = "files/InventoryManagementObservers.dat"; } } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h index 7585abc..b908f51 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h @@ -12,28 +12,28 @@ Date: 19-May-2026 namespace util { - enum class UserType + enum class UserType : int { ADMIN, TECHNICIAN, CUSTOMER }; - enum class PaymentMode + enum class PaymentMode : int { ONLINE, OFFLINE, NOTSET }; - enum class PaymentStatus + enum class PaymentStatus : int { PENDING, COMPLETED, PAID }; - enum class ServiceJobStatus + enum class ServiceJobStatus : int { PENDING, STARTED, @@ -42,7 +42,7 @@ namespace util CANCELLED }; - enum class State + enum class State : int { ACTIVE, INACTIVE diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h index 2b3e94a..c8bf96f 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h @@ -106,4 +106,4 @@ namespace util file << records[index] << '\n'; } } -} +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h index 75d8bbc..dffc199 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace util { @@ -54,6 +55,48 @@ namespace util value = cleanedValue; } + /* + * Function: readPassword + * Description: Reads a password from console without echoing characters; + * displays '*' for each character typed, handles backspace, + * and cleans commas from the result. + * Parameters: + * value - reference to a string where the password will be stored + * Returns: + * void - no return value + */ + inline void readPassword(std::string& value) + { + value.clear(); + char currentCharacter; + while ((currentCharacter = _getch()) != '\r') + { + if (currentCharacter == '\b') + { + if (!value.empty()) + { + value.pop_back(); + std::cout << "\b \b"; + } + } + else + { + value += currentCharacter; + std::cout << '*'; + } + } + std::cout << std::endl; + std::string cleanedValue; + for (int iterator = 0; iterator < value.length(); iterator++) + { + if (value[iterator] != ',') + { + cleanedValue += value[iterator]; + } + } + value = cleanedValue; + } + /* * Function: pressEnter * Description: Pauses execution until the user presses Enter. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h index 300d5c4..9b6bc9d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h @@ -99,4 +99,80 @@ namespace util auto observerIDs = service->getObserverIDs(); util::saveRecords(filePath, observerIDs); } + + template + Map getObjects(const Map>& trackedRecords); + + template + Map getConstObjects(const Map>& trackedRecords); + + template + TrackedRecord createNewRecord(TObject* object); +} + +/* +Function: getObjects +Description: Extracts the object pointers from a tracked-record + collection and returns them as a map keyed by the + same identifiers. +Parameters: + - trackedRecords: Collection of tracked records. +Returns: + - Map: Collection of object pointers. +*/ +template +util::Map util::getObjects(const util::Map>& trackedRecords) +{ + util::Map objects; + for (int index = 0; index < trackedRecords.getSize(); ++index) + { + const std::string& key = trackedRecords.getKeyAt(index); + TObject* object = trackedRecords.getValueAt(index).data; + objects.insert(key, object); + } + return objects; +} + +/* +Function: getConstObjects +Description: Extracts the object pointers from a tracked-record + collection and returns them as a read-only map + keyed by the same identifiers. +Parameters: + - trackedRecords: Collection of tracked records. +Returns: + - Map: + Collection of read-only object pointers. +*/ +template +util::Map util::getConstObjects( + const util::Map>& trackedRecords) +{ + util::Map objects; + for (int index = 0; index < trackedRecords.getSize(); ++index) + { + const std::string& key = trackedRecords.getKeyAt(index); + const TObject* object = trackedRecords.getValueAt(index).data; + objects.insert(key, object); + } + return objects; +} + +/* +Function: createNewRecord +Description: Creates a tracked record for a newly created + object. The record is initialized with + NEW_RECORD state. +Parameters: + - object: Pointer to the newly created object. +Returns: + - TrackedRecord: Initialized tracked record. +*/ +template +TrackedRecord util::createNewRecord(TObject* object) +{ + TrackedRecord record; + record.data = object; + record.state = RecordState::NEW_RECORD; + return record; } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp index 3e56d99..0d60f97 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp @@ -30,7 +30,6 @@ Description: Displays the customer menu and handles user input until logout is s Parameter: None Return type: void */ - void CustomerMenu::showMenu() { while (true) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 83720c0..c6ebaa6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -914,7 +914,7 @@ inline void changePasswordHelper(Controller& controller) util::clear(); std::cout << "Change Password\n"; std::cout << "Enter new password: "; - util::read(newPassword); + util::readPassword(newPassword); if (!util::isPasswordValid(newPassword)) { std::cout << "Error: Password is not strong enough!\n"; @@ -928,7 +928,7 @@ inline void changePasswordHelper(Controller& controller) continue; } std::cout << "Confirm new password: "; - util::read(confirmedPassword); + util::readPassword(confirmedPassword); if (confirmedPassword != newPassword) { std::cout << "Passwords are different. Try again\n"; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h index a17ccb1..2d118a0 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h @@ -22,4 +22,4 @@ public: void viewNotifications(); void logout(); void changePassword(); -}; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp index 841e2ea..4596d9b 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp @@ -106,7 +106,7 @@ void UserInterface::login() std::cout << "Enter username: "; util::read(username); std::cout << "Enter password: "; - util::read(password); + util::readPassword(password); if (m_controller.login(username, password)) { const User* authenticatedUser = m_controller.getAuthenticatedUser(); @@ -167,7 +167,7 @@ void UserInterface::registerCustomer() return; } std::cout << "Enter password: "; - util::read(password); + util::readPassword(password); if (!util::isPasswordValid(password)) { std::cout << "Error: Password is invalid!"; @@ -185,4 +185,4 @@ void UserInterface::registerCustomer() m_controller.createCustomer(username, name, password, email, phone); std::cout << "Registration is successful"; util::pressEnter(); -} +} \ No newline at end of file From 929f609f2436988a64baeacbf3f5dc6cb0594d6f Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Fri, 12 Jun 2026 03:19:43 +0530 Subject: [PATCH 02/39] Implement Model Refactoring 1959: Model Refactoring UserStory #1959 1. Replaced CSV-based User serialization and deserialization with SerializedUser record-based serialization for shared memory storage. 2. Implemented User::serialize() to convert User objects into fixed-size SerializedUser structures containing user details and enum values. 3. Implemented User::deserialize() to reconstruct User objects directly from SerializedUser records. 4. Updated User class interfaces to use SerializedUser types instead of std::string serialization APIs. 5. Removed legacy CSV serialization support, including CSV parsing logic and header generation functionality. 6. Added SerializedUser dependencies through SerializedRecords.h inclusion and forward declaration support. N/A Sreeja Reghukumar, please review --- .../models/User.cpp | 82 +++++++------------ .../models/User.h | 6 +- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp index 0b4e86e..c4dc928 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp @@ -8,6 +8,7 @@ Date: 19-May-2026 */ #include +#include "SerializedRecords.h" #include "User.h" #include "Notification.h" #include "Enums.h" @@ -28,7 +29,8 @@ Returns: User::User() : m_id("USR" + std::to_string(++m_uid)), m_type(util::UserType::CUSTOMER), - m_status(util::State::ACTIVE) {} + m_status(util::State::ACTIVE) { +} /* Function: User @@ -51,7 +53,8 @@ User::User(const std::string& userName, const std::string& password, const std:: m_phone(phone), m_email(email), m_type(role), - m_status(util::State::ACTIVE) {} + m_status(util::State::ACTIVE) { +} /* Function: User (parameterized constructor with ID) @@ -324,68 +327,43 @@ void User::setState(util::State status) /* Function: serialize -Description: Serializes the user into a CSV-formatted string. +Description: Serializes the User object into a SerializedUser record. Parameters: - None Returns: - - std::string: Serialized user record + - SerializedUser: Serialized representation of the user */ -std::string User::serialize() const +SerializedUser User::serialize() const { - std::ostringstream serializedUser; - serializedUser << m_id << ',' - << m_userName << ',' - << m_password << ',' - << m_name << ',' - << m_phone << ',' - << m_email << ',' - << util::getUserTypeString(m_type) << ',' - << util::getStateString(m_status); - return serializedUser.str(); + SerializedUser serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.username, sizeof(serialized.username), m_userName.c_str()); + strcpy_s(serialized.password, sizeof(serialized.password), m_password.c_str()); + strcpy_s(serialized.name, sizeof(serialized.name), m_name.c_str()); + strcpy_s(serialized.phone, sizeof(serialized.phone), m_phone.c_str()); + strcpy_s(serialized.email, sizeof(serialized.email), m_email.c_str()); + serialized.userType = m_type; + serialized.status = m_status; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a User object. +Description: Deserializes a SerializedUser record into a User object. Parameters: - - record: const std::string&, serialized user record + - serializedUser: const SerializedUser&, serialized user record Returns: - User*: Pointer to the deserialized User object */ -User* User::deserialize(const std::string& record) +User* User::deserialize(const SerializedUser& serializedUser) { - std::string id, name, username, phone, password, email; - std::string userTypeString, stateString; - std::istringstream serializedUser(record); - getline(serializedUser, id, ','); - getline(serializedUser, username, ','); - getline(serializedUser, password, ','); - getline(serializedUser, name, ','); - getline(serializedUser, phone, ','); - getline(serializedUser, email, ','); - getline(serializedUser, userTypeString, ','); - getline(serializedUser, stateString); - util::UserType userType = util::getUserType(userTypeString); - util::State status = util::getState(stateString); - return Factory::getObject(id, - username, - password, - name, - phone, - email, - userType, - status); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for user serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,Username,Password,Name,Phone,Email,UserType,UserStatus") -*/ -std::string User::getHeaders() -{ - return "ID,Username,Password,Name,Phone,Email,UserType,UserStatus"; + return Factory::getObject( + serializedUser.id, + serializedUser.username, + serializedUser.password, + serializedUser.name, + serializedUser.phone, + serializedUser.email, + serializedUser.userType, + serializedUser.status); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h index 12923f6..1026bd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h @@ -14,6 +14,7 @@ Date: 19-May-2026 #include "Enums.h" class Notification; +struct SerializedUser; class User : public Observer { @@ -51,7 +52,6 @@ public: void addNotification(Notification* notification) override; void setRole(util::UserType role); void setState(util::State status); - std::string serialize() const; - static User* deserialize(const std::string&); - static std::string getHeaders(); + SerializedUser serialize() const; + static User* deserialize(const SerializedUser& serializedUser); }; From 4b76cae358acbe3395e24ecc0da24e4522ddb471 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Fri, 12 Jun 2026 03:22:13 +0530 Subject: [PATCH 03/39] Implement Model Refactoring 1955: Model Refactoring UserStory #1955 1. Replaced CSV-based serialization and deserialization in ComboPackage, JobCard, Service, and ServiceBooking models with fixed-size SerializedRecord structures for shared memory storage. 2. Implemented serialize() methods to convert objects into SerializedComboPackage, SerializedJobCard, SerializedService, and SerializedServiceBooking records. 3. Implemented deserialize() methods to reconstruct objects directly from SerializedRecord types instead of parsing CSV strings. 4. Updated model class interfaces to use SerializedRecord types, removing legacy CSV serialization APIs and header generation functions. 5. Added SerializedRecords.h dependencies and forward declarations for Serialized structures across affected models. N/A Sreeja Reghukumar, please review --- .../models/ComboPackage.cpp | 76 +++++---------- .../models/ComboPackage.h | 6 +- .../models/JobCard.cpp | 89 ++++++----------- .../models/JobCard.h | 16 ++-- .../models/Service.cpp | 76 +++++---------- .../models/Service.h | 7 +- .../models/ServiceBooking.cpp | 95 ++++++------------- .../models/ServiceBooking.h | 7 +- 8 files changed, 119 insertions(+), 253 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.cpp index 4e69d9f..7e9d1a9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.cpp @@ -9,6 +9,7 @@ Date: 19-May-2026 #include #include +#include "SerializedRecords.h" #include "ComboPackage.h" #include "Service.h" #include "Factory.h" @@ -28,7 +29,8 @@ Returns: ComboPackage::ComboPackage() : m_id("CMP" + std::to_string(++m_uid)), m_status(util::State::ACTIVE), - m_discountPercentage(0.0) {} + m_discountPercentage(0.0) { +} /* Function: ComboPackage @@ -270,72 +272,38 @@ static util::Vector getServiceIDsAsVector(const std::string& servic /* Function: serialize -Description: Serializes the combo package into a CSV-formatted string. +Description: Serializes the ComboPackage object into a SerializedComboPackage record. Parameters: - None Returns: - - std::string: Serialized combo package record + - SerializedComboPackage: Serialized representation of the combo package */ -std::string ComboPackage::serialize() const +SerializedComboPackage ComboPackage::serialize() const { - std::ostringstream serializedComboPackage; - serializedComboPackage << m_id << ',' - << m_packageName << ',' - << m_discountPercentage << ',' - << getServiceIDsAsString(m_serviceIDs) << ',' - << util::getStateString(m_status); - return serializedComboPackage.str(); + SerializedComboPackage serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.packageName, sizeof(serialized.packageName), m_packageName.c_str()); + strcpy_s(serialized.serviceIDs, sizeof(serialized.serviceIDs), getServiceIDsAsString(m_serviceIDs).c_str()); + serialized.discountPercentage = m_discountPercentage; + serialized.status = m_status; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a ComboPackage object. +Description: Deserializes a SerializedComboPackage record into a ComboPackage object. Parameters: - - record: const std::string&, serialized combo package record + - serializedComboPackage: const SerializedComboPackage&, serialized combo package record Returns: - ComboPackage*: Pointer to the deserialized ComboPackage object -Throws: - - std::runtime_error if data is invalid */ -ComboPackage* ComboPackage::deserialize(const std::string& record) +ComboPackage* ComboPackage::deserialize(const SerializedComboPackage& serializedComboPackage) { - std::string id, packageName; - std::string discountPercentageString, serviceIDsString, statusString; - double discountPercentage; - std::istringstream serializedComboPackage(record); - getline(serializedComboPackage, id, ','); - getline(serializedComboPackage, packageName, ','); - getline(serializedComboPackage, discountPercentageString, ','); - getline(serializedComboPackage, serviceIDsString, ','); - getline(serializedComboPackage, statusString, ','); - try - { - discountPercentage = std::stod(discountPercentageString); - } - catch (...) - { - throw std::runtime_error("Invalid combo package data"); - } - util::Vector serviceIDs = getServiceIDsAsVector(serviceIDsString); - util::State status = util::getState(statusString); + util::Vector serviceIDs = getServiceIDsAsVector(serializedComboPackage.serviceIDs); return Factory::getObject( - id, - packageName, - discountPercentage, + serializedComboPackage.id, + serializedComboPackage.packageName, + serializedComboPackage.discountPercentage, serviceIDs, - status - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for combo package serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,PackageName,DiscountPercentage,ServiceIDs,Status") -*/ -std::string ComboPackage::getHeaders() -{ - return "ID,PackageName,DiscountPercentage,ServiceIDs,Status"; -} + serializedComboPackage.status); +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h index 941c2b3..4e29310 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h @@ -12,6 +12,7 @@ Date: 19-May-2026 #include "Enums.h" class Service; +class SerializedComboPackage; class ComboPackage { @@ -38,7 +39,6 @@ public: void setDiscountPercentage(double discountPercentage); void setServices(const util::Map& services); void setState(util::State status); - std::string serialize() const; - static ComboPackage* deserialize(const std::string&); - static std::string getHeaders(); + SerializedComboPackage serialize() const; + static ComboPackage* deserialize(const SerializedComboPackage&); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp index 214a8e0..0bea7c2 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp @@ -9,6 +9,7 @@ Date:19-May-2026 #include #include +#include "SerializedRecords.h" #include "JobCard.h" #include "Factory.h" #include "StringHelper.h" @@ -28,7 +29,8 @@ JobCard::JobCard() m_booking(nullptr), m_service(nullptr), m_technician(nullptr), - m_status(util::ServiceJobStatus()) {} + m_status(util::ServiceJobStatus()) { +} /* Function: JobCard @@ -65,7 +67,8 @@ JobCard::JobCard(const std::string& bookingId, m_technician(technician), m_assignedDate(assignedDate), m_status(status), - m_completionDate(completionDate) {} + m_completionDate(completionDate) { +} /* Function: JobCard (parameterized constructor with ID) @@ -351,79 +354,41 @@ void JobCard::setCompletionDate(const util::Timestamp& completionDate) /* Function: serialize -Description: Serializes the job card into a CSV-formatted string. +Description: Serializes the JobCard object into a SerializedJobCard record. Parameters: - None Returns: - - std::string: Serialized job card record + - SerializedJobCard: Serialized representation of the job card */ -std::string JobCard::serialize() const +SerializedJobCard JobCard::serialize() const { - std::ostringstream serializedJobCard; - serializedJobCard << m_id << ',' - << m_bookingId << ',' - << m_serviceId << ',' - << m_technicianId << ',' - << m_assignedDate.toString() << ',' - << util::getServiceJobStatusString(m_status) << ',' - << m_completionDate.toString(); - return serializedJobCard.str(); + SerializedJobCard serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.bookingId, sizeof(serialized.bookingId), m_bookingId.c_str()); + strcpy_s(serialized.serviceId, sizeof(serialized.serviceId), m_serviceId.c_str()); + strcpy_s(serialized.technicianId, sizeof(serialized.technicianId), m_technicianId.c_str()); + serialized.assignedDate = m_assignedDate; + serialized.status = m_status; + serialized.completionDate = m_completionDate; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a JobCard object. +Description: Deserializes a SerializedJobCard record into a JobCard object. Parameters: - - record: const std::string&, serialized job card record + - serializedJobCard: const SerializedJobCard&, serialized job card record Returns: - JobCard*: Pointer to the deserialized JobCard object -Throws: - - std::runtime_error if timestamp parsing fails */ -JobCard* JobCard::deserialize(const std::string& record) +JobCard* JobCard::deserialize(const SerializedJobCard& serializedJobCard) { - std::string id, bookingId, serviceId, technicianId; - std::string assignedDateString, statusString, completionDateString; - std::istringstream serializedJobCard(record); - getline(serializedJobCard, id, ','); - getline(serializedJobCard, bookingId, ','); - getline(serializedJobCard, serviceId, ','); - getline(serializedJobCard, technicianId, ','); - getline(serializedJobCard, assignedDateString, ','); - getline(serializedJobCard, statusString, ','); - getline(serializedJobCard, completionDateString, ','); - util::Timestamp assignedDate; - util::Timestamp completionDate; - try - { - assignedDate = util::Timestamp::fromString(assignedDateString); - completionDate = util::Timestamp::fromString(completionDateString); - } - catch (...) - { - throw std::runtime_error("Invalid timestamp"); - } - util::ServiceJobStatus status = util::getServiceJobStatus(statusString); return Factory::getObject( - id, - bookingId, - serviceId, - technicianId, - assignedDate, - status, - completionDate - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for job card serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,BookingID,ServiceID,TechnicianID,AssignedDate,Status,CompletionDate") -*/ -std::string JobCard::getHeaders() -{ - return "ID,BookingID,ServiceID,TechnicianID,AssignedDate,Status,CompletionDate"; + serializedJobCard.id, + serializedJobCard.bookingId, + serializedJobCard.serviceId, + serializedJobCard.technicianId, + serializedJobCard.assignedDate, + serializedJobCard.status, + serializedJobCard.completionDate); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h index a845263..ed243e8 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h @@ -15,6 +15,7 @@ Date:19-May-2026 class ServiceBooking; class Service; class User; +struct SerializedJobCard; class JobCard { @@ -34,11 +35,11 @@ public: JobCard(); JobCard(const std::string& bookingId, ServiceBooking* booking, - Service* service, - const std::string& serviceId, - const std::string& technicianId, - User* technician, - const util::Timestamp& assignedDate, + Service* service, + const std::string& serviceId, + const std::string& technicianId, + User* technician, + const util::Timestamp& assignedDate, util::ServiceJobStatus status, const util::Timestamp& completionDate ); @@ -70,7 +71,6 @@ public: void setAssignedDate(const util::Timestamp& assignedDate); void setStatus(util::ServiceJobStatus status); void setCompletionDate(const util::Timestamp& completionDate); - std::string serialize() const; - static JobCard* deserialize(const std::string&); - static std::string getHeaders(); + SerializedJobCard serialize() const; + static JobCard* deserialize(const SerializedJobCard&); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.cpp index 0717467..fc39531 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.cpp @@ -8,6 +8,7 @@ Date: 19-May-2026 */ #include +#include "SerializedRecords.h" #include "Service.h" #include "InventoryItem.h" #include "StringHelper.h" @@ -27,7 +28,8 @@ Returns: Service::Service() : m_id("SRV" + std::to_string(++m_uid)), m_status(util::State::ACTIVE), - m_laborCost(0.0) {} + m_laborCost(0.0) { +} /* Function: Service @@ -44,7 +46,7 @@ Service::Service(const std::string& name, const util::Map getInventoryItemIDsAsVector(const std::string& /* Function: serialize -Description: Serializes the service into a CSV-formatted string. +Description: Serializes the Service object into a SerializedService record. Parameters: - None Returns: - - std::string: Serialized service record + - SerializedService: Serialized representation of the service */ -std::string Service::serialize() const +SerializedService Service::serialize() const { - std::ostringstream serializedService; - serializedService << m_id << ',' - << m_name << ',' - << getInventoryItemIDsAsString(m_requiredInventoryItemIDs) << ',' - << m_laborCost << ',' - << util::getStateString(m_status); - return serializedService.str(); + SerializedService serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.name, sizeof(serialized.name), m_name.c_str()); + strcpy_s(serialized.inventoryItemIDs, sizeof(serialized.inventoryItemIDs), getInventoryItemIDsAsString(m_requiredInventoryItemIDs).c_str()); + serialized.laborCost = m_laborCost; + serialized.status = m_status; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a Service object. +Description: Deserializes a SerializedService record into a Service object. Parameters: - - record: const std::string&, serialized service record + - serializedService: const SerializedService&, serialized service record Returns: - Service*: Pointer to the deserialized Service object -Throws: - - std::runtime_error if labor cost parsing fails */ -Service* Service::deserialize(const std::string& record) +Service* Service::deserialize(const SerializedService& serializedService) { - std::string id, name; - std::string inventoryItemIDsString, laborCostString, statusString; - double laborCost; - std::istringstream serializedService(record); - getline(serializedService, id, ','); - getline(serializedService, name, ','); - getline(serializedService, inventoryItemIDsString, ','); - getline(serializedService, laborCostString, ','); - getline(serializedService, statusString, ','); - util::Vector inventoryItemIDs = getInventoryItemIDsAsVector(inventoryItemIDsString); - try - { - laborCost = std::stod(laborCostString); - } - catch (...) - { - throw std::runtime_error("Invalid labor cost"); - } - util::State status = util::getState(statusString); + util::Vector inventoryItemIDs = getInventoryItemIDsAsVector(serializedService.inventoryItemIDs); return Factory::getObject( - id, - name, + serializedService.id, + serializedService.name, inventoryItemIDs, - laborCost, - status - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for service serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,Name,InventoryIDs,LaborCost,Status") -*/ -std::string Service::getHeaders() -{ - return "ID,Name,InventoryIDs,LaborCost,Status"; + serializedService.laborCost, + serializedService.status); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.h index b80e674..bff13d6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Service.h @@ -6,7 +6,6 @@ Author: Trenser Date: 19-May-2026 */ - #pragma once #include #include "Map.h" @@ -14,6 +13,7 @@ Date: 19-May-2026 #include "Enums.h" class InventoryItem; +struct SerializedService; class Service { @@ -40,7 +40,6 @@ public: void setRequiredInventoryItems(const util::Map& requiredInventoryItems); void setLaborCost(double laborCost); void setState(util::State status); - std::string serialize() const; - static Service* deserialize(const std::string&); - static std::string getHeaders(); + SerializedService serialize() const; + static Service* deserialize(const SerializedService&); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp index 31b9d87..bd7d4d1 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp @@ -6,8 +6,10 @@ Description: Implementation file containing the method definitions of the Author: Trenser Date:19-May-2026 */ + #include #include +#include "SerializedRecords.h" #include "ServiceBooking.h" #include "Service.h" #include "Enums.h" @@ -28,7 +30,8 @@ ServiceBooking::ServiceBooking() m_customer(nullptr), m_assignedTechnician(nullptr), m_status(util::ServiceJobStatus::PENDING), - m_discountPercentage(0.0) {} + m_discountPercentage(0.0) { +} /* Function: ServiceBooking @@ -437,84 +440,46 @@ static util::Vector getServiceIDsAsVector(const std::string& servic /* Function: serialize -Description: Serializes the service booking into a CSV-formatted string. +Description: Serializes the ServiceBooking object into a SerializedServiceBooking record. Parameters: - None Returns: - - std::string: Serialized booking record + - SerializedServiceBooking: Serialized representation of the service booking */ -std::string ServiceBooking::serialize() const +SerializedServiceBooking ServiceBooking::serialize() const { - std::ostringstream serializedBooking; - serializedBooking << m_id << ',' - << util::getServiceJobStatusString(m_status) << ',' - << getServiceIDsAsString(m_serviceIDs) << ',' - << m_customerId << ',' - << m_vehicleNumber << ',' - << m_vehicleBrand << ',' - << m_vehicleModel << ',' - << m_assignedTechnicianId << ',' - << m_discountPercentage << ','; - return serializedBooking.str(); + SerializedServiceBooking serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.serviceIDs, sizeof(serialized.serviceIDs), getServiceIDsAsString(m_serviceIDs).c_str()); + strcpy_s(serialized.customerId, sizeof(serialized.customerId), m_customerId.c_str()); + strcpy_s(serialized.vehicleNumber, sizeof(serialized.vehicleNumber), m_vehicleNumber.c_str()); + strcpy_s(serialized.vehicleBrand, sizeof(serialized.vehicleBrand), m_vehicleBrand.c_str()); + strcpy_s(serialized.vehicleModel, sizeof(serialized.vehicleModel), m_vehicleModel.c_str()); + strcpy_s(serialized.assignedTechnicianId, sizeof(serialized.assignedTechnicianId), m_assignedTechnicianId.c_str()); + serialized.status = m_status; + serialized.discountPercentage = m_discountPercentage; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a ServiceBooking object. +Description: Deserializes a SerializedServiceBooking record into a ServiceBooking object. Parameters: - - record: const std::string&, serialized booking record + - serializedServiceBooking: const SerializedServiceBooking&, serialized service booking record Returns: - ServiceBooking*: Pointer to the deserialized ServiceBooking object -Throws: - - std::runtime_error if discount percentage parsing fails */ -ServiceBooking* ServiceBooking::deserialize(const std::string& record) +ServiceBooking* ServiceBooking::deserialize(const SerializedServiceBooking& serializedServiceBooking) { - std::string id, customerId, vehicleNumber, vehicleBrand, vehicleModel, assignedTechnicianId; - std::string serviceJobStatusString, serviceIDsString, discountPercentageString; - double discountPercentage; - std::istringstream serializedBooking(record); - getline(serializedBooking, id, ','); - getline(serializedBooking, serviceJobStatusString, ','); - getline(serializedBooking, serviceIDsString, ','); - getline(serializedBooking, customerId, ','); - getline(serializedBooking, vehicleNumber, ','); - getline(serializedBooking, vehicleBrand, ','); - getline(serializedBooking, vehicleModel, ','); - getline(serializedBooking, assignedTechnicianId, ','); - getline(serializedBooking, discountPercentageString, ','); - util::Vector serviceIDs = getServiceIDsAsVector(serviceIDsString); - try - { - discountPercentage = std::stod(discountPercentageString); - } - catch (...) - { - throw std::runtime_error("Invalid discount percentage"); - } - util::ServiceJobStatus status = util::getServiceJobStatus(serviceJobStatusString); + util::Vector serviceIDs = getServiceIDsAsVector(serializedServiceBooking.serviceIDs); return Factory::getObject( - id, - status, + serializedServiceBooking.id, + serializedServiceBooking.status, serviceIDs, - customerId, - vehicleNumber, - vehicleBrand, - vehicleModel, - assignedTechnicianId, - discountPercentage - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for service booking serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,Status,ServiceIDs,CustomerID,VehicleNumber,VehicleBrand,VehicleModel,AssignedTechnicianID,DiscountPercentage") -*/ -std::string ServiceBooking::getHeaders() -{ - return "ID,Status,ServiceIDs,CustomerID,VehicleNumber,VehicleBrand,VehicleModel,AssignedTechnicianID,DiscountPercentage"; + serializedServiceBooking.customerId, + serializedServiceBooking.vehicleNumber, + serializedServiceBooking.vehicleBrand, + serializedServiceBooking.vehicleModel, + serializedServiceBooking.assignedTechnicianId, + serializedServiceBooking.discountPercentage); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h index 24acb32..433d9d1 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h @@ -6,6 +6,7 @@ Description: Header file declaring the ServiceBooking class, which represents Author: Trenser Date:19-May-2026 */ + #pragma once #include #include "Map.h" @@ -14,6 +15,7 @@ Date:19-May-2026 class Service; class User; +struct SerializedServiceBooking; class ServiceBooking { @@ -78,7 +80,6 @@ public: void setAssignedTechnicianId(const std::string& assignedTechnicianId); void setAssignedTechnician(User* assignedTechnician); void setDiscountPercentage(double discountPercentage); - std::string serialize() const; - static ServiceBooking* deserialize(const std::string&); - static std::string getHeaders(); + SerializedServiceBooking serialize() const; + static ServiceBooking* deserialize(const SerializedServiceBooking&); }; \ No newline at end of file From 6dea303b923346001e5d97b96523b521e5498f0a Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Fri, 12 Jun 2026 11:09:35 +0530 Subject: [PATCH 04/39] Implement Model Refactoring 1951: Model Refactoring UserStory #1951 1. Replaced CSV-based serialization and deserialization in ComboPackage, JobCard, Service, and ServiceBooking models with fixed-size SerializedRecord structures for shared memory storage. 2. Implemented serialize() methods to convert objects into SerializedComboPackage, SerializedJobCard, SerializedService, and SerializedServiceBooking records. 3. Implemented deserialize() methods to reconstruct objects directly from SerializedRecord types instead of parsing CSV strings. 4. Updated model class interfaces to use SerializedRecord types, removing legacy CSV serialization APIs and header generation functions. 5. Added SerializedRecords.h dependencies and forward declarations for Serialized structures across affected models. N/A Sreeja Reghukumar --- .../models/User.cpp | 82 +++++++------------ .../models/User.h | 6 +- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp index 0b4e86e..c4dc928 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp @@ -8,6 +8,7 @@ Date: 19-May-2026 */ #include +#include "SerializedRecords.h" #include "User.h" #include "Notification.h" #include "Enums.h" @@ -28,7 +29,8 @@ Returns: User::User() : m_id("USR" + std::to_string(++m_uid)), m_type(util::UserType::CUSTOMER), - m_status(util::State::ACTIVE) {} + m_status(util::State::ACTIVE) { +} /* Function: User @@ -51,7 +53,8 @@ User::User(const std::string& userName, const std::string& password, const std:: m_phone(phone), m_email(email), m_type(role), - m_status(util::State::ACTIVE) {} + m_status(util::State::ACTIVE) { +} /* Function: User (parameterized constructor with ID) @@ -324,68 +327,43 @@ void User::setState(util::State status) /* Function: serialize -Description: Serializes the user into a CSV-formatted string. +Description: Serializes the User object into a SerializedUser record. Parameters: - None Returns: - - std::string: Serialized user record + - SerializedUser: Serialized representation of the user */ -std::string User::serialize() const +SerializedUser User::serialize() const { - std::ostringstream serializedUser; - serializedUser << m_id << ',' - << m_userName << ',' - << m_password << ',' - << m_name << ',' - << m_phone << ',' - << m_email << ',' - << util::getUserTypeString(m_type) << ',' - << util::getStateString(m_status); - return serializedUser.str(); + SerializedUser serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.username, sizeof(serialized.username), m_userName.c_str()); + strcpy_s(serialized.password, sizeof(serialized.password), m_password.c_str()); + strcpy_s(serialized.name, sizeof(serialized.name), m_name.c_str()); + strcpy_s(serialized.phone, sizeof(serialized.phone), m_phone.c_str()); + strcpy_s(serialized.email, sizeof(serialized.email), m_email.c_str()); + serialized.userType = m_type; + serialized.status = m_status; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a User object. +Description: Deserializes a SerializedUser record into a User object. Parameters: - - record: const std::string&, serialized user record + - serializedUser: const SerializedUser&, serialized user record Returns: - User*: Pointer to the deserialized User object */ -User* User::deserialize(const std::string& record) +User* User::deserialize(const SerializedUser& serializedUser) { - std::string id, name, username, phone, password, email; - std::string userTypeString, stateString; - std::istringstream serializedUser(record); - getline(serializedUser, id, ','); - getline(serializedUser, username, ','); - getline(serializedUser, password, ','); - getline(serializedUser, name, ','); - getline(serializedUser, phone, ','); - getline(serializedUser, email, ','); - getline(serializedUser, userTypeString, ','); - getline(serializedUser, stateString); - util::UserType userType = util::getUserType(userTypeString); - util::State status = util::getState(stateString); - return Factory::getObject(id, - username, - password, - name, - phone, - email, - userType, - status); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for user serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,Username,Password,Name,Phone,Email,UserType,UserStatus") -*/ -std::string User::getHeaders() -{ - return "ID,Username,Password,Name,Phone,Email,UserType,UserStatus"; + return Factory::getObject( + serializedUser.id, + serializedUser.username, + serializedUser.password, + serializedUser.name, + serializedUser.phone, + serializedUser.email, + serializedUser.userType, + serializedUser.status); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h index 12923f6..1026bd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h @@ -14,6 +14,7 @@ Date: 19-May-2026 #include "Enums.h" class Notification; +struct SerializedUser; class User : public Observer { @@ -51,7 +52,6 @@ public: void addNotification(Notification* notification) override; void setRole(util::UserType role); void setState(util::State status); - std::string serialize() const; - static User* deserialize(const std::string&); - static std::string getHeaders(); + SerializedUser serialize() const; + static User* deserialize(const SerializedUser& serializedUser); }; From 89fc66218116d8d9a72aac641ead194724835956 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Fri, 12 Jun 2026 11:31:03 +0530 Subject: [PATCH 05/39] Implement Service Refactoring 1960: Service Refactoring UserStory #1960 1. Replaced the Controller load/save workflow with initialize() and shutdown() methods. 2. Moved startup checks such as admin verification, low stock alerts, and payment reminders into Controller::initialize(). 3. Updated UserInterface to use the new Controller initialization and shutdown flow. 4. Updated DataStore::getUsers() and DataStore::getNotifications() to load data directly from shared memory. 5. Added logic to rebuild user notification mappings when user data is loaded from shared memory. 6. Implemented user and notification persistence through DataStore::saveUsers() and DataStore::saveNotifications(). 7. Updated UserManagementService to use TrackedRecord-based shared memory records. 8. Added DataStore locking and unlocking to user management operations for synchronization. 9. Updated createUser(), updateUserDetails(), removeUser(), and deleteNotification() to persist changes through shared memory. 10. Removed legacy loadUsers() and saveUsers() implementations from UserManagementService. 11. Added required header dependencies for shared memory support. N/A Sreeja Reghukumar, please review --- .../Trenser.VehicleServiceSystem.vcxproj | 1 + .../controllers/Controller.cpp | 72 +++---- .../controllers/Controller.h | 5 +- .../datastores/DataStore.cpp | 21 +++ .../datastores/DataStoreLockGuard.h | 28 +++ .../datastores/sharedmemory/SharedMemory.cpp | 1 + .../services/UserManagementService.cpp | 177 ++++++++---------- .../services/UserManagementService.h | 2 - .../views/UserInterface.cpp | 9 +- 9 files changed, 163 insertions(+), 153 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 6a269f5..7c4bfd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -157,6 +157,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp index 9bc1d86..487ecd5 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp @@ -588,63 +588,37 @@ void Controller::configureNotifications(bool paymentNotifications, bool serviceN } /* -Function: loadSystemData -Description: Loads all system data from persistent storage into memory. - Invokes the respective management services to load users, inventory items, services, - combo packages, service bookings, job cards, invoices, and observers. +Function: initialize +Description: Initializes the system and run system checks to ensure critical configurations, such as verifying admin existence. Parameters: - None Returns: - - void + - bool */ -void Controller::loadSystemData() +bool Controller::initialize() { - m_userManagementService.loadUsers(); - m_inventoryManagementService.loadInventoryItems(); - m_serviceManagementService.loadServices(); - m_serviceManagementService.loadComboPackages(); - m_serviceManagementService.loadServiceBookings(); - m_serviceManagementService.loadJobCards(); - m_paymentManagementService.loadInvoices(); - m_serviceManagementService.loadObservers(); - m_paymentManagementService.loadObservers(); - m_inventoryManagementService.loadObservers(); -} + auto& dataStore = DataStore::getInstance(); -/* -Function: saveSystemData -Description: Saves all system data from memory back to persistent storage. - Invokes the respective management services to save users, inventory items, services, - combo packages, service bookings, job cards, invoices, and observers. -Parameters: - - None -Returns: - - void -*/ -void Controller::saveSystemData() -{ - m_userManagementService.saveUsers(); - m_inventoryManagementService.saveInventoryItems(); - m_serviceManagementService.saveServices(); - m_serviceManagementService.saveComboPackages(); - m_serviceManagementService.saveServiceBookings(); - m_serviceManagementService.saveJobCards(); - m_paymentManagementService.saveInvoices(); - m_serviceManagementService.saveObservers(); - m_paymentManagementService.saveObservers(); - m_inventoryManagementService.saveObservers(); -} - -/* -Function: runSystemChecks -Description: Runs system checks to ensure critical configurations, such as verifying admin existence. -Parameter: None -Return type: void -*/ -void Controller::runSystemChecks() -{ + if (!dataStore.initialize()) + { + return false; + } m_userManagementService.ensureAdminExists(); m_inventoryManagementService.sendLowStockAlerts(); m_paymentManagementService.sendPaymentReminders(); + return true; } +/* +Function: shutdown +Description: Shutdown the system, and do necessary cleanups +Parameters: + - None +Returns: + - void +*/ +void Controller::shutdown() +{ + auto& dataStore = DataStore::getInstance(); + dataStore.shutdown(); +} diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h index 35241a0..7ec1c30 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h @@ -70,7 +70,6 @@ public: util::Vector getNotifications(); void deleteNotification(const std::string& notificationID); void configureNotifications(bool paymentNotifications, bool serviceNotifications); - void loadSystemData(); - void saveSystemData(); - void runSystemChecks(); + bool initialize(); + void shutdown(); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 1cade32..bda9ac0 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -228,6 +228,22 @@ Returns: */ util::Map>& DataStore::getUsers() { + auto users = loadRecords(m_users); + refreshCache(m_userCache, users); + auto& notifications = getNotifications(); + int numberOfNotifications = m_notificationCache.getSize(); + for (int index = 0; index < numberOfNotifications; index++) + { + Notification* notification = notifications.getValueAt(index).data; + const std::string& recipientUserId = notification->getRecipientUserId(); + int userIndex = m_userCache.find(recipientUserId); + if (userIndex == -1) + { + throw std::runtime_error("Invalid recipient user ID"); + } + User* user = m_userCache.getValueAt(userIndex).data; + user->addNotification(notification); + } return m_userCache; } @@ -241,6 +257,8 @@ Returns: */ util::Map>& DataStore::getNotifications() { + auto notifications = loadRecords(m_notifications); + refreshCache(m_notificationCache, notifications); return m_notificationCache; } @@ -371,6 +389,8 @@ Returns: */ void DataStore::saveUsers() { + saveRecords(m_users, m_userCache); + saveNotifications(); } /* @@ -383,6 +403,7 @@ Returns: */ void DataStore::saveNotifications() { + saveRecords(m_notifications, m_notificationCache); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h new file mode 100644 index 0000000..2e04eb0 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h @@ -0,0 +1,28 @@ +/* +File: DataStoreLockGuard.h +Description: Defines the DataStoreLockGuard class used to manage DataStore + locking and unlocking automatically within a scope. +Author: Trenser +Date: 12-June-2026 +*/ + +#pragma once +#include "DataStore.h" + +class DataStoreLockGuard +{ +public: + explicit DataStoreLockGuard(DataStore& dataStore) + : m_dataStore(dataStore) + { + m_dataStore.lockDataStore(); + } + ~DataStoreLockGuard() + { + m_dataStore.unlockDataStore(); + } + DataStoreLockGuard(const DataStoreLockGuard&) = delete; + DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete; +private: + DataStore& m_dataStore; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp index 6a10c9f..4b151e3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp @@ -10,6 +10,7 @@ Created: 11-June-2026 */ #include "SharedMemory.h" +#include "Windows.h" #include "Config.h" /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 8ba68d0..3eeead8 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -20,6 +20,9 @@ Date:19-May-2026 #include "UserManagementService.h" #include "Vector.h" #include "Validator.h" +#include "Utility.h" +#include "TrackedRecord.h" +#include "DataStoreLockGuard.h" /* Function: ensureAdminExists @@ -31,12 +34,13 @@ Return type: void */ void UserManagementService::ensureAdminExists() { + DataStoreLockGuard lock(m_dataStore); auto& usersMap = m_dataStore.getUsers(); int usersMapSize = usersMap.getSize(); bool isAdminFound = false; for (int index = 0; index < usersMapSize; index++) { - User* user = usersMap.getValueAt(index); + User* user = usersMap.getValueAt(index).data; if (user && user->getUserType() == util::UserType::ADMIN) { isAdminFound = true; @@ -73,7 +77,9 @@ void UserManagementService::createUser(const std::string& username, const std::s InventoryManagementService inventoryManagementService; PaymentManagementService paymentManagementService; ServiceManagementService serviceManagementService; - auto& usersMap = m_dataStore.getUsers(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + auto usersMap = util::getObjects(trackedUsersMap); if (util::isUsernameDuplicate(username, usersMap)) { throw std::runtime_error("Username already exists"); @@ -87,13 +93,14 @@ void UserManagementService::createUser(const std::string& username, const std::s throw std::runtime_error("Phone already exists"); } User* newUser = Factory::getObject(username, password, name, phone, email, type); - usersMap.insert(newUser->getId(), newUser); + trackedUsersMap.insert(newUser->getId(), util::createNewRecord(newUser)); paymentManagementService.attach(newUser); serviceManagementService.attach(newUser); if (newUser->getUserType() == util::UserType::ADMIN) { inventoryManagementService.attach(newUser); } + m_dataStore.saveUsers(); } /* @@ -107,19 +114,24 @@ Return type: void */ void UserManagementService::updateUserDetails(const std::string& userID, const std::string& email, const std::string& phone) { - auto& usersMap = m_dataStore.getUsers(); - int index = usersMap.find(userID); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + auto usersMap = util::getObjects(trackedUsersMap); + int index = trackedUsersMap.find(userID); if (index == -1) { throw std::runtime_error("User does not exist!\n"); } - User* user = usersMap.getValueAt(index); + User* user = trackedUsersMap.getValueAt(index).data; + bool isModified = false; if (email != user->getEmail()) { if (util::isEmailDuplicate(email, usersMap)) { throw std::runtime_error("Email already exists!\n"); } + user->setEmail(email); + isModified = true; } if (phone != user->getPhone()) { @@ -127,9 +139,14 @@ void UserManagementService::updateUserDetails(const std::string& userID, const s { throw std::runtime_error("Phone number already exists!\n"); } + user->setPhone(phone); + isModified = true; + } + if (isModified) + { + trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; + m_dataStore.saveUsers(); } - user->setEmail(email); - user->setPhone(phone); } /* @@ -144,12 +161,13 @@ Throws: */ util::Vector UserManagementService::getUserNotifications(const std::string& userID) { - auto& usersMap = m_dataStore.getUsers(); - if (usersMap.find(userID) == -1) + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + if (trackedUsersMap.find(userID) == -1) { throw std::runtime_error("No user found with given UserID"); } - User* user = usersMap[userID]; + User* user = trackedUsersMap[userID].data; if (user) { auto& notifications = user->getNotifications(); @@ -159,6 +177,7 @@ util::Vector UserManagementService::getUserNotifications(const st { notificationsVector.push_back(notifications.getValueAt(index)); } + m_dataStore.unlockDataStore(); return notificationsVector; } else @@ -169,97 +188,41 @@ util::Vector UserManagementService::getUserNotifications(const st /* Function: deleteNotification -Description: Deletes a specific notification associated with a given user ID. +Description: Marks a specific notification associated with a given user + as inactive. Parameters: - notificationID: The unique ID of the notification to be deleted. - userID: The unique ID of the user whose notification is to be deleted. Returns: - void Throws: - - std::runtime_error if no user is found with the given UserID or if no notification is found with the given NotificationID. + - std::runtime_error if no user is found with the given UserID or + if no notification is found with the given NotificationID. */ void UserManagementService::deleteNotification(const std::string& notificationID, const std::string& userID) { - auto& usersMap = m_dataStore.getUsers(); - if (usersMap.find(userID) == -1) + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + int userIndex = trackedUsersMap.find(userID); + if (userIndex == -1) { throw std::runtime_error("No user found with given UserID"); } - User* user = usersMap[userID]; + User* user = trackedUsersMap.getValueAt(userIndex).data; auto& notifications = user->getNotifications(); if (notifications.find(notificationID) == -1) { throw std::runtime_error("No notification found with given NotificationID"); } - notifications.remove(notificationID); -} - -/* -Function: loadUsers -Description: Loads users and notifications from persistent storage into the datastore. - Validates that each notification’s recipient exists and attaches the - notification to the corresponding user. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if a notification recipient user ID is invalid -*/ -void UserManagementService::loadUsers() -{ - util::FileManager userFileManager(config::file::USER_FILE); - util::FileManager notificationFileManager(config::file::NOTIFICATION_FILE); - auto& users = m_dataStore.getUsers(); - auto usersMap = userFileManager.load(); - auto notificationsMap = notificationFileManager.load(); - int numberOfUsers = usersMap.getSize(); - int numberOfNotifications = notificationsMap.getSize(); - for (int index = 0; index < numberOfUsers; index++) - { - users[usersMap.getKeyAt(index)] = usersMap.getValueAt(index); - } - for (int index = 0; index < numberOfNotifications; index++) - { - Notification* notification = notificationsMap.getValueAt(index); - const std::string& recipientUserId = notification->getRecipientUserId(); - int userIndex = users.find(recipientUserId); - if (userIndex == -1) - { - throw std::runtime_error("Invalid recipient user ID"); - } - User* user = users.getValueAt(userIndex); - user->addNotification(notification); - } -} - -/* -Function: saveUsers -Description: Saves users and their notifications from the datastore to persistent storage. - Collects notifications from all users into a single map before saving. -Parameters: - - None -Returns: - - void -*/ -void UserManagementService::saveUsers() -{ - util::FileManager userFileManager(config::file::USER_FILE); - util::FileManager notificationFileManager(config::file::NOTIFICATION_FILE); - auto& users = m_dataStore.getUsers(); - util::Map notifications; - for (int userIndex = 0; userIndex < users.getSize(); userIndex++) - { - User* user = users.getValueAt(userIndex); - auto& userNotifications = user->getNotifications(); - for (int notificationIndex = 0; notificationIndex < userNotifications.getSize(); notificationIndex++) - { - notifications[userNotifications.getKeyAt(notificationIndex)] = - userNotifications.getValueAt(notificationIndex); - } - } - userFileManager.save(users); - notificationFileManager.save(notifications); + int notificationIndex = trackedNotificationsMap.find(notificationID); + if (notificationIndex == -1) + { + throw std::runtime_error("No notification found with given NotificationID"); + } + notifications[notificationID]->setState(util::State::INACTIVE); + trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED; + m_dataStore.saveNotifications(); } /* @@ -270,7 +233,9 @@ Return type: util::Map */ util::Map UserManagementService::getUsers() { - return m_dataStore.getUsers(); + DataStoreLockGuard lock(m_dataStore); + auto users = util::getObjects(m_dataStore.getUsers()); + return users; } /* @@ -281,10 +246,12 @@ Return type: User* */ User* UserManagementService::getUser(const std::string& userID) { - int index = m_dataStore.getUsers().find(userID); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + int index = trackedUsersMap.find(userID); if (index != -1) { - return m_dataStore.getUsers().getValueAt(index); + return trackedUsersMap.getValueAt(index).data; } return nullptr; } @@ -300,35 +267,53 @@ void UserManagementService::removeUser(const std::string& userID) InventoryManagementService inventoryManagementService; PaymentManagementService paymentManagementService; ServiceManagementService serviceManagementService; - int index = m_dataStore.getUsers().find(userID); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + int index = trackedUsersMap.find(userID); if (index != -1) { - User* user = m_dataStore.getUsers().getValueAt(index); + User* user = trackedUsersMap.getValueAt(index).data; if (user != nullptr) { if (user->getUserType() == util::UserType::CUSTOMER) { - serviceManagementService.cancelCustomerServiceBookings(userID); + serviceManagementService.cancelCustomerServiceBookings(userID); } if (user->getUserType() == util::UserType::TECHNICIAN) { serviceManagementService.cancelTechnicianJobs(userID); } - user->setState(util::State::INACTIVE); inventoryManagementService.detach(user); paymentManagementService.detach(user); serviceManagementService.detach(user); + user->setState(util::State::INACTIVE); + trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; + m_dataStore.saveUsers(); } } } -util::Map UserManagementService::getUsers(util::UserType type) +/* +Function: getUsers +Description: Retrieves all active users of the specified type from + the DataStore. +Parameters: + - type: The user type to filter by + (ADMIN, CUSTOMER, or TECHNICIAN). +Returns: + - util::Map: + Collection of active users matching the specified type, + keyed by user ID. +*/ +util::Map UserManagementService::getUsers(util::UserType type) { - util::Map& currentUsers = m_dataStore.getUsers(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + util::Map currentUsers = util::getObjects(trackedUsersMap); util::Map filteredUsersMap; - for (int iterator = 0; iterator < currentUsers.getSize(); iterator++) + for (int index = 0; index < currentUsers.getSize(); index++) { - User* currentUser = currentUsers.getValueAt(iterator); + User* currentUser = currentUsers.getValueAt(index); if (currentUser && currentUser->getState() == util::State::ACTIVE && currentUser->getUserType() == type) { filteredUsersMap.insert(currentUser->getId(), currentUser); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h index b448872..28aa446 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.h @@ -31,6 +31,4 @@ public: util::Vector getUserNotifications(const std::string& userID); void deleteNotification(const std::string& notificationID, const std::string& userID); void ensureAdminExists(); - void loadUsers(); - void saveUsers(); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp index 4596d9b..926fc9e 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/UserInterface.cpp @@ -27,8 +27,11 @@ void UserInterface::run() { try { - m_controller.loadSystemData(); - m_controller.runSystemChecks(); + if (!m_controller.initialize()) + { + std::cout << "Error: Failed to initialize the system!"; + return; + } bool isMenuActive = true; while (isMenuActive) { @@ -49,7 +52,7 @@ void UserInterface::run() util::pressEnter(); } } - m_controller.saveSystemData(); + m_controller.shutdown(); } catch (const std::invalid_argument& exception) { From 74dbbd9e82a1dc53e13f1e987b0a51d2d3be29e8 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Fri, 12 Jun 2026 14:28:48 +0530 Subject: [PATCH 06/39] Implement Service Refactoring 1952: Service Refactoring UserStory #1952 1. Enhanced DataStore::getUsers to load SerializedUser records, refresh cache, and attach notifications to recipient users with validation for recipient IDs. 2. Updated DataStore::saveUsers to persist SerializedUser records and save notifications alongside user data. 3. Refactored AuthenticationManagementService::login to use DataStoreLockGuard and tracked user map with SerializedUser-backed records. 4. Modified AuthenticationManagementService::changePassword to ensure thread-safe updates, mark user record as MODIFIED, and persist changes via DataStore::saveUsers. 5. Added dependencies for Utility.h and DataStoreLockGuard.h in AuthenticationManagementService.cpp to support safe record handling. N/A Sreeja Reghukumar --- .../Trenser.VehicleServiceSystem.vcxproj | 1 + ...enser.VehicleServiceSystem.vcxproj.filters | 3 ++ .../datastores/DataStore.cpp | 18 ++++++++++++ .../datastores/DataStoreLockGuard.h | 28 +++++++++++++++++++ .../AuthenticationManagementService.cpp | 20 ++++++++++--- 5 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 6a269f5..7c4bfd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -157,6 +157,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index 7e74b19..94301c4 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -278,5 +278,8 @@ Header Files\DataStores\SharedMemory + + Header Files\DataStores + \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 1cade32..f430184 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -228,6 +228,22 @@ Returns: */ util::Map>& DataStore::getUsers() { + auto users = loadRecords(m_users); + refreshCache(m_userCache, users); + auto& notifications = getNotifications(); + int numberOfNotifications = m_notificationCache.getSize(); + for (int index = 0; index < numberOfNotifications; index++) + { + Notification* notification = notifications.getValueAt(index).data; + const std::string& recipientUserId = notification->getRecipientUserId(); + int userIndex = m_userCache.find(recipientUserId); + if (userIndex == -1) + { + throw std::runtime_error("Invalid recipient user ID"); + } + User* user = m_userCache.getValueAt(userIndex).data; + user->addNotification(notification); + } return m_userCache; } @@ -371,6 +387,8 @@ Returns: */ void DataStore::saveUsers() { + saveRecords(m_users, m_userCache); + saveNotifications(); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h new file mode 100644 index 0000000..2e04eb0 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h @@ -0,0 +1,28 @@ +/* +File: DataStoreLockGuard.h +Description: Defines the DataStoreLockGuard class used to manage DataStore + locking and unlocking automatically within a scope. +Author: Trenser +Date: 12-June-2026 +*/ + +#pragma once +#include "DataStore.h" + +class DataStoreLockGuard +{ +public: + explicit DataStoreLockGuard(DataStore& dataStore) + : m_dataStore(dataStore) + { + m_dataStore.lockDataStore(); + } + ~DataStoreLockGuard() + { + m_dataStore.unlockDataStore(); + } + DataStoreLockGuard(const DataStoreLockGuard&) = delete; + DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete; +private: + DataStore& m_dataStore; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp index dc3a94c..27be89f 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp @@ -10,6 +10,8 @@ Date:19-May-2026 #include #include "AuthenticationManagementService.h" #include "User.h" +#include "Utility.h" +#include "DataStoreLockGuard.h" User* AuthenticationManagementService::m_authenticatedUser = nullptr; @@ -24,11 +26,12 @@ Return type: bool - true if login successful, false otherwise */ bool AuthenticationManagementService::login(const std::string& username, const std::string& password) { - util::Map users = m_dataStore.getUsers(); - int usersMapSize = users.getSize(); - for (int index = 0; index < usersMapSize; index++) + DataStoreLockGuard lock(m_dataStore); + auto& trackedUserMap = m_dataStore.getUsers(); + int trackedUserMapSize = trackedUserMap.getSize(); + for (int index = 0; index < trackedUserMapSize; index++) { - User* user = users.getValueAt(index); + User* user = trackedUserMap.getValueAt(index).data; if (username == user->getUserName()) { if (password == user->getPassword()) @@ -74,9 +77,18 @@ Return type: void */ void AuthenticationManagementService::changePassword(const std::string& newPassword) { + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + int index = trackedUsersMap.find(m_authenticatedUser->getId()); + if (index == -1) + { + throw std::runtime_error("User does not exist!\n"); + } if (m_authenticatedUser == nullptr) { throw std::runtime_error("There is no user currently logged in!"); } m_authenticatedUser->setPassword(newPassword); + trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; + m_dataStore.saveUsers(); } From b98062d45c5585a1bbf2c0042a81f1af21645283 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Fri, 12 Jun 2026 15:30:34 +0530 Subject: [PATCH 07/39] Implement Model Refactoring 1957: Model Refactoring UserStory #1957 1. Added SerializedRecords.h dependency and forward declaration for SerializedInventoryItem to support fixed-size record storage. 2. Replaced CSV-based serialization in InventoryItem with serialize() method returning SerializedInventoryItem structure. 3. Replaced CSV-based deserialization logic with deserialize() method that reconstructs InventoryItem directly from SerializedInventoryItem record. 4. Removed legacy CSV parsing, header generation, and exception handling tied to string-based serialization. 5. Updated InventoryItem class interface in InventoryItem.h to use SerializedInventoryItem types instead of std::string serialization APIs. N/A Sreeja Reghukumar --- .../models/InventoryItem.cpp | 79 ++++++------------- .../models/InventoryItem.h | 8 +- 2 files changed, 27 insertions(+), 60 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.cpp index f34b8a5..67fb7ba 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.cpp @@ -8,6 +8,7 @@ Date: 19-May-2026 #include #include +#include "SerializedRecords.h" #include "Factory.h" #include "StringHelper.h" #include "InventoryItem.h" @@ -27,7 +28,8 @@ InventoryItem::InventoryItem() : m_id("IIM" + std::to_string(++m_uid)), m_quantity(0), m_status(util::State::ACTIVE), - m_price(0.0) {} + m_price(0.0) { +} /* Function: InventoryItem @@ -45,7 +47,8 @@ InventoryItem::InventoryItem(const std::string& partName, int quantity, double p m_partName(partName), m_quantity(quantity), m_status(util::State::ACTIVE), - m_price(price) {} + m_price(price) { +} /* Function: InventoryItem (parameterized constructor with ID) @@ -206,73 +209,37 @@ void InventoryItem::setState(util::State status) /* Function: serialize -Description: Serializes the inventory item into a CSV-formatted string. +Description: Serializes the InventoryItem object into a SerializedInventoryItem record. Parameters: - None Returns: - - std::string: Serialized inventory item record + - SerializedInventoryItem: Serialized representation of the inventory item */ -std::string InventoryItem::serialize() const +SerializedInventoryItem InventoryItem::serialize() const { - std::ostringstream serializedInventoryItem; - serializedInventoryItem << m_id << ',' - << m_partName << ',' - << m_quantity << ',' - << m_price << ',' - << util::getStateString(m_status); - return serializedInventoryItem.str(); + SerializedInventoryItem serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.partName, sizeof(serialized.partName), m_partName.c_str()); + serialized.quantity = m_quantity; + serialized.price = m_price; + serialized.status = m_status; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into an InventoryItem object. +Description: Deserializes a SerializedInventoryItem record into an InventoryItem object. Parameters: - - record: const std::string&, serialized inventory item record + - serializedInventoryItem: const SerializedInventoryItem&, serialized inventory item record Returns: - InventoryItem*: Pointer to the deserialized InventoryItem object -Throws: - - std::runtime_error if data is invalid */ -InventoryItem* InventoryItem::deserialize(const std::string& record) +InventoryItem* InventoryItem::deserialize(const SerializedInventoryItem& serializedInventoryItem) { - std::string id, partName; - std::string quantityString, priceString, statusString; - int quantity; - double price; - std::istringstream serializedInventoryItem(record); - getline(serializedInventoryItem, id, ','); - getline(serializedInventoryItem, partName, ','); - getline(serializedInventoryItem, quantityString, ','); - getline(serializedInventoryItem, priceString, ','); - getline(serializedInventoryItem, statusString, ','); - try - { - quantity = std::stoi(quantityString); - price = std::stod(priceString); - } - catch (...) - { - throw std::runtime_error("Invalid inventory item data"); - } - util::State status = util::getState(statusString); return Factory::getObject( - id, - partName, - quantity, - price, - status - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for inventory item serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,PartName,Quantity,Price,Status") -*/ -std::string InventoryItem::getHeaders() -{ - return "ID,PartName,Quantity,Price,Status"; + serializedInventoryItem.id, + serializedInventoryItem.partName, + serializedInventoryItem.quantity, + serializedInventoryItem.price, + serializedInventoryItem.status); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.h index 5808e8f..90b716a 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/InventoryItem.h @@ -6,11 +6,12 @@ Author: Trenser Date: 19-May-2026 */ - #pragma once #include #include "Enums.h" +struct SerializedInventoryItem; + class InventoryItem { private: @@ -34,7 +35,6 @@ public: void setQuantity(int quantity); void setPrice(double price); void setState(util::State status); - std::string serialize() const; - static InventoryItem* deserialize(const std::string&); - static std::string getHeaders(); + SerializedInventoryItem serialize() const; + static InventoryItem* deserialize(const SerializedInventoryItem&); }; \ No newline at end of file From f0c7d27e6cf2b239f12ea76ce956d2dc4bcb93ec Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Fri, 12 Jun 2026 18:29:10 +0530 Subject: [PATCH 08/39] Implement Service Refactoring 1958: Service Refactoring UserStory #1958 1. Added DataStoreLockGuard.h include to project file and InventoryManagementService for thread-safe datastore operations. 2. Enhanced DataStore::getUsers to load SerializedUser records, refresh cache, and attach notifications with recipient validation. 3. Enhanced DataStore::getInventoryItems to load SerializedInventoryItem records and refresh cache for tracked inventory items. 4. Refactored InventoryManagementService::sendLowStockAlerts to use tracked inventory and user maps with DataStoreLockGuard, ensuring safe access. 5. Removed legacy observer management, load/save inventory items, and related persistence functions from InventoryManagementService. 6. Updated InventoryManagementService::addInventoryItem to insert new records into tracked inventory map and persist changes via saveInventoryItems. 7. Updated InventoryManagementService::addInventoryItemStock to validate item existence, update quantity, mark record as MODIFIED, and persist changes. 8. Refactored InventoryManagementService::getInventoryItems to return object map extracted from tracked records with DataStoreLockGuard. 9. Updated InventoryManagementService::removeInventoryItem to validate item ID, mark state as INACTIVE, set record state to MODIFIED, and persist changes. 10. Updated InventoryManagementService::getInventoryItem to safely retrieve inventory items from tracked records with error handling. N/A Sreeja Reghukumar --- .../Trenser.VehicleServiceSystem.vcxproj | 1 + .../datastores/DataStore.cpp | 19 ++ .../datastores/DataStoreLockGuard.h | 28 +++ .../services/InventoryManagementService.cpp | 181 ++++++------------ .../services/InventoryManagementService.h | 2 - 5 files changed, 107 insertions(+), 124 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 6a269f5..7c4bfd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -157,6 +157,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 1cade32..a7d41d9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -228,6 +228,22 @@ Returns: */ util::Map>& DataStore::getUsers() { + auto users = loadRecords(m_users); + refreshCache(m_userCache, users); + auto& notifications = getNotifications(); + int numberOfNotifications = m_notificationCache.getSize(); + for (int index = 0; index < numberOfNotifications; index++) + { + Notification* notification = notifications.getValueAt(index).data; + const std::string& recipientUserId = notification->getRecipientUserId(); + int userIndex = m_userCache.find(recipientUserId); + if (userIndex == -1) + { + throw std::runtime_error("Invalid recipient user ID"); + } + User* user = m_userCache.getValueAt(userIndex).data; + user->addNotification(notification); + } return m_userCache; } @@ -280,6 +296,8 @@ Returns: */ util::Map>& DataStore::getInventoryItems() { + auto inventoryItems = loadRecords(m_inventoryItems); + refreshCache(m_inventoryItemCache, inventoryItems); return m_inventoryItemCache; } @@ -419,6 +437,7 @@ Returns: */ void DataStore::saveInventoryItems() { + saveRecords(m_inventoryItems, m_inventoryItemCache); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h new file mode 100644 index 0000000..2e04eb0 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h @@ -0,0 +1,28 @@ +/* +File: DataStoreLockGuard.h +Description: Defines the DataStoreLockGuard class used to manage DataStore + locking and unlocking automatically within a scope. +Author: Trenser +Date: 12-June-2026 +*/ + +#pragma once +#include "DataStore.h" + +class DataStoreLockGuard +{ +public: + explicit DataStoreLockGuard(DataStore& dataStore) + : m_dataStore(dataStore) + { + m_dataStore.lockDataStore(); + } + ~DataStoreLockGuard() + { + m_dataStore.unlockDataStore(); + } + DataStoreLockGuard(const DataStoreLockGuard&) = delete; + DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete; +private: + DataStore& m_dataStore; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp index d08d957..bdc4e39 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -19,7 +19,7 @@ Date: 22-May-2026 #include "User.h" #include "Utility.h" #include "Vector.h" - +#include "DataStoreLockGuard.h" util::Map InventoryManagementService::m_observers{}; @@ -58,18 +58,19 @@ Returns: */ void InventoryManagementService::sendLowStockAlerts() { - auto& inventoryItems = m_dataStore.getInventoryItems(); - if (inventoryItems.isEmpty()) + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemsMap = m_dataStore.getInventoryItems(); + auto& trackedUserMap = m_dataStore.getUsers(); + if (trackedInventoryItemsMap.isEmpty()) { return; } - int inventoryItemsSize = inventoryItems.getSize(); - auto& usersMap = m_dataStore.getUsers(); - int usersMapSize = usersMap.getSize(); + int inventoryItemsSize = trackedInventoryItemsMap.getSize(); + int usersMapSize = trackedUserMap.getSize(); util::Vector adminUsers; for (int index = 0; index < usersMapSize; index++) { - User* user = usersMap.getValueAt(index); + User* user = trackedUserMap.getValueAt(index).data; if (user->getUserType() == util::UserType::ADMIN) { adminUsers.push_back(user); @@ -82,7 +83,7 @@ void InventoryManagementService::sendLowStockAlerts() } for (int index = 0; index < inventoryItemsSize; index++) { - InventoryItem* inventoryItem = inventoryItems.getValueAt(index); + InventoryItem* inventoryItem = trackedInventoryItemsMap.getValueAt(index).data; if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD) { sendLowStockAlertsToAdmins(*this, inventoryItem, adminUsers); @@ -90,95 +91,6 @@ void InventoryManagementService::sendLowStockAlerts() } } -/* -Function: getObserverIDs -Description: Retrieves the IDs of all observers currently attached to the - InventoryManagementService. -Parameters: - - None -Returns: - - util::Vector: Vector of observer user IDs -*/ -util::Vector InventoryManagementService::getObserverIDs() -{ - util::Vector observerIDs; - int numberOfObservers = m_observers.getSize(); - for (int index = 0; index < numberOfObservers; index++) - { - User* observer = m_observers.getValueAt(index); - if (observer) - { - observerIDs.push_back(observer->getId()); - } - } - return observerIDs; -} - -/* -Function: loadInventoryItems -Description: Loads inventory items from persistent storage into the datastore. - Uses FileManager to deserialize inventory items from the configured file. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::loadInventoryItems() -{ - util::FileManager inventoryItemFileManager(config::file::INVENTORYITEM_FILE); - auto& inventoryItems = m_dataStore.getInventoryItems(); - auto inventoryItemsMap = inventoryItemFileManager.load(); - int numberOfInventoryItems = inventoryItemsMap.getSize(); - for (int index = 0; index < numberOfInventoryItems; index++) - { - inventoryItems[inventoryItemsMap.getKeyAt(index)] = inventoryItemsMap.getValueAt(index); - } -} - -/* -Function: saveInventoryItems -Description: Saves inventory items from the datastore to persistent storage. - Uses FileManager to serialize inventory items into the configured file. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::saveInventoryItems() -{ - util::FileManager inventoryItemFileManager(config::file::INVENTORYITEM_FILE); - auto& inventoryItems = m_dataStore.getInventoryItems(); - inventoryItemFileManager.save(inventoryItems); -} - -/* -Function: loadObservers -Description: Loads observer IDs from persistent storage and attaches corresponding - users as observers to the InventoryManagementService. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::loadObservers() -{ - util::loadObservers(config::file::INVENTORYMANAGEMENTOBSERVERS, this, m_dataStore); -} - -/* -Function: saveObservers -Description: Saves the current observer IDs of the InventoryManagementService - to persistent storage for future retrieval. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::saveObservers() -{ - util::saveObservers(config::file::INVENTORYMANAGEMENTOBSERVERS, this); -} - /* Function: addInventoryItem Description: Creates a new inventory item using the Factory and inserts it @@ -190,8 +102,11 @@ Return type: void */ void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price) { + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); InventoryItem* newItem = Factory::getObject(partName, quantity, price); - m_dataStore.getInventoryItems().insert(newItem->getId(), newItem); + trackedInventoryItemMap.insert(newItem->getId(), util::createNewRecord(newItem)); + m_dataStore.saveInventoryItems(); } /* @@ -203,16 +118,22 @@ Return type: void */ void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity) { - int index = m_dataStore.getInventoryItems().find(selectedItemId); - if (index != -1) - { - InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index); - if (item != nullptr) - { - int totalQuantity = item->getQuantity() + quantity; - item->setQuantity(totalQuantity); - } - } + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); + int index = trackedInventoryItemMap.find(selectedItemId); + if (index == -1) + { + throw std::runtime_error("Inventory update failed: Item ID '" + selectedItemId + "' not found."); + } + InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data; + if (item == nullptr) + { + throw std::runtime_error("Inventory update failed. Item does not exist.\n"); + } + int totalQuantity = item->getQuantity() + quantity; + item->setQuantity(totalQuantity); + trackedInventoryItemMap.getValueAt(index).state = RecordState::MODIFIED; + m_dataStore.saveInventoryItems(); } /* @@ -223,7 +144,10 @@ Return type: util::Map */ util::Map InventoryManagementService::getInventoryItems() { - return m_dataStore.getInventoryItems(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); + auto inventoryMap = util::getObjects(trackedInventoryItemMap); + return inventoryMap; } /* @@ -234,15 +158,21 @@ Return type: void */ void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID) { - int index = m_dataStore.getInventoryItems().find(inventoryItemID); - if (index != -1) - { - InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index); - if (item != nullptr) - { - item->setState(util::State::INACTIVE); - } - } + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); + int index = trackedInventoryItemMap.find(inventoryItemID); + if (index == -1) + { + throw std::runtime_error("Inventory removal failed: Item ID '" + inventoryItemID + "' not found."); + } + InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data; + if (item == nullptr) + { + throw std::runtime_error("Inventory removal failed: Item ID does not exist."); + } + item->setState(util::State::INACTIVE); + trackedInventoryItemMap.getValueAt(index).state = RecordState::MODIFIED; + m_dataStore.saveInventoryItems(); } /* @@ -253,12 +183,19 @@ Return type: InventoryItem* */ InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID) { - int index = m_dataStore.getInventoryItems().find(inventoryItemID); - if (index != -1) + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); + int index = trackedInventoryItemMap.find(inventoryItemID); + if (index == -1) { - return m_dataStore.getInventoryItems().getValueAt(index); + return nullptr; } - return nullptr; + InventoryItem* inventoryItem = trackedInventoryItemMap.getValueAt(index).data; + if (inventoryItem == nullptr) + { + throw std::runtime_error("Item ID does not exist."); + } + return inventoryItem; } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h index f5db383..bb7cda1 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h @@ -33,8 +33,6 @@ public: void sendNotification(User* user, const std::string& title, const std::string& message) override; void attach(User* user) override; void detach(User* user) override; - void loadInventoryItems(); - void saveInventoryItems(); void loadObservers(); void saveObservers(); }; From f3220ee191d220c8581677e842e0b2ce2abe4d31 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Fri, 12 Jun 2026 17:37:37 +0530 Subject: [PATCH 09/39] Implement Service Refactoring 1927: Implement Service Refactorings UserStory #1927 1. Added DataStoreLockGuard integration across ServiceManagementService methods for thread-safe datastore operations. 2. Updated DataStore::getServices(), getComboPackages(), getServiceBookings(), and getJobCards() to enrich records with linked entities (inventory items, services, bookings, technicians). 3. Modified ServiceManagementService::purchaseService() and purchaseComboPackage() to use tracked records and persist bookings safely. 4. Enhanced restoreInventory() and processBookingCancellation() to handle tracked records, update record states, and restore inventory items correctly. 5. Refactored cancelCustomerServiceBookings() and cancelTechnicianJobs() to use tracked records, restore inventory, and persist changes. 6. Updated createComboPackage(), removeComboPackage(), and createJobCard() to use tracked records and save changes to datastore. 7. Updated createService() to validate inventory items with tracked records and persist new services safely. 8. Added required header dependencies for DataStoreLockGuard and shared memory support. N/A Sreeja Reghukumar, please review --- .../Trenser.VehicleServiceSystem.vcxproj | 1 + ...enser.VehicleServiceSystem.vcxproj.filters | 3 + .../datastores/DataStore.cpp | 135 ++++++++++++ .../datastores/DataStore.h | 1 + .../datastores/DataStoreLockGuard.h | 28 +++ .../services/ServiceManagementService.cpp | 196 ++++++++++++------ 6 files changed, 301 insertions(+), 63 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 6a269f5..7c4bfd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -157,6 +157,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index 7e74b19..94301c4 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -278,5 +278,8 @@ Header Files\DataStores\SharedMemory + + Header Files\DataStores + \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 1cade32..0f71bce 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -12,6 +12,8 @@ Date: 19-May-2026 #include "Config.h" #include "SerializedRecords.h" #include "FileHelper.h" +#include "ServiceBooking.h" +#include "JobCard.h" /* Function: DataStore @@ -254,6 +256,28 @@ Returns: */ util::Map>& DataStore::getServices() { + util::Map> services = loadRecords(m_services); + refreshCache(m_serviceCache, services); + util::Map>& inventoryItems = getInventoryItems(); + size_t numberOfServices = m_serviceCache.getSize(); + for (int iteratorOne =0; iteratorOne < numberOfServices; iteratorOne++) + { + Service* currentService = m_serviceCache.getValueAt(iteratorOne).data; + util::Map inventoryItemMap; + util::Vector currentServiceInventoryItem = currentService->getRequiredInventoryItemIDs(); + for (int iteratorTwo = 0; iteratorTwo < currentServiceInventoryItem.getSize(); iteratorTwo++) + { + const std::string& currentInventoryItemId = currentServiceInventoryItem[iteratorTwo]; + int currentInventoryItemIndex = inventoryItems.find(currentInventoryItemId); + if (currentInventoryItemIndex == -1) + { + throw std::runtime_error("Invalid inventory item ID"); + } + InventoryItem* currentItem = inventoryItems.getValueAt(currentInventoryItemIndex).data; + inventoryItemMap[currentInventoryItemId] = currentItem; + } + currentService->setRequiredInventoryItems(inventoryItemMap); + } return m_serviceCache; } @@ -267,6 +291,28 @@ Returns: */ util::Map>& DataStore::getComboPackages() { + util::Map> comboPackages = loadRecords(m_comboPackages); + refreshCache(m_comboPackageCache, comboPackages); + util::Map>& services = getServices(); + size_t numberOfComboPackages = m_comboPackageCache.getSize(); + for (int iteratorOne = 0; iteratorOne < numberOfComboPackages; iteratorOne++) + { + ComboPackage* currentComboPackage = m_comboPackageCache.getValueAt(iteratorOne).data; + util::Vector currentServiceIds = currentComboPackage->getServiceIDs(); + util::Map currentComboPackageServices; + for (int iteratorTwo = 0; iteratorTwo < currentServiceIds.getSize(); iteratorTwo++) + { + const std::string& currentServiceId = currentServiceIds[iteratorTwo]; + int serviceIndex = services.find(currentServiceId); + if (serviceIndex == -1) + { + throw std::runtime_error("Invalid service ID"); + } + Service* currentService = services.getValueAt(serviceIndex).data; + currentComboPackageServices[currentServiceId] = currentService; + } + currentComboPackage->setServices(currentComboPackageServices); + } return m_comboPackageCache; } @@ -293,6 +339,49 @@ Returns: */ util::Map>& DataStore::getServiceBookings() { + util::Map> serviceBookings = loadRecords(m_serviceBookings); + refreshCache(m_serviceBookingCache, serviceBookings); + auto& users = getUsers(); + auto& services = getServices(); + size_t numberOfServiceBookings = m_serviceBookingCache.getSize(); + for (int iteratorOne = 0; iteratorOne < numberOfServiceBookings; iteratorOne++) + { + ServiceBooking* serviceBooking = m_serviceBookingCache.getValueAt(iteratorOne).data; + auto& serviceIds = serviceBooking->getServiceIDs(); + util::Map servicesInBooking; + for (int iteratorTwo = 0; iteratorTwo < serviceIds.getSize(); iteratorTwo++) + { + const std::string& currentServiceId = serviceIds[iteratorTwo]; + int serviceIndex = services.find(currentServiceId); + if (serviceIndex == -1) + { + throw std::runtime_error("Invalid service index."); + } + auto currentService = services.getValueAt(serviceIndex); + servicesInBooking[currentServiceId] = currentService.data; + } + serviceBooking->setServices(servicesInBooking); + if (!serviceBooking->getCustomerId().empty()) + { + int userIndex = users.find(serviceBooking->getCustomerId()); + if (userIndex == -1) + { + throw std::runtime_error("Invalid user index."); + } + auto customer = users.getValueAt(userIndex); + serviceBooking->setCustomer(customer.data); + } + if (!serviceBooking->getAssignedTechnicianId().empty()) + { + int technicianIndex = users.find(serviceBooking->getAssignedTechnicianId()); + if (technicianIndex == -1) + { + throw std::runtime_error("Invalid technician index."); + } + auto technician = users.getValueAt(technicianIndex); + serviceBooking->setAssignedTechnician(technician.data); + } + } return m_serviceBookingCache; } @@ -306,9 +395,51 @@ Returns: */ util::Map>& DataStore::getJobCards() { + util::Map> jobCards = loadRecords(m_jobCards); + refreshCache(m_jobCardCache, jobCards); + auto& serviceBookings = getServiceBookings(); + auto& services = getServices(); + auto& users = getUsers(); + int numberOfJobCards = m_jobCardCache.getSize(); + for (int iterator = 0; iterator < numberOfJobCards; iterator++) + { + JobCard* jobCard = m_jobCardCache.getValueAt(iterator).data; + if (!jobCard) + { + continue; + } + const std::string& bookingId = jobCard->getBookingId(); + int bookingIndex = serviceBookings.find(bookingId); + if (bookingIndex == -1) + { + throw std::runtime_error("Invalid booking ID: " + bookingId); + } + auto trackedBooking = serviceBookings.getValueAt(bookingIndex); + jobCard->setBooking(trackedBooking.data); + const std::string& serviceId = jobCard->getServiceId(); + int serviceIndex = services.find(serviceId); + if (serviceIndex == -1) + { + throw std::runtime_error("Invalid service ID: " + serviceId); + } + auto trackedService = services.getValueAt(serviceIndex); + jobCard->setService(trackedService.data); + const std::string& technicianId = jobCard->getTechnicianId(); + if (!technicianId.empty()) + { + int technicianIndex = users.find(technicianId); + if (technicianIndex == -1) + { + throw std::runtime_error("Invalid technician ID: " + technicianId); + } + auto trackedTechnician = users.getValueAt(technicianIndex); + jobCard->setTechnician(trackedTechnician.data); + } + } return m_jobCardCache; } + /* Function: getInvoices Description: Retrieves all invoice records from the datastore. @@ -395,6 +526,7 @@ Returns: */ void DataStore::saveServices() { + saveRecords(m_services, m_serviceCache); } /* @@ -407,6 +539,7 @@ Returns: */ void DataStore::saveComboPackages() { + saveRecords(m_comboPackages, m_comboPackageCache); } /* @@ -431,6 +564,7 @@ Returns: */ void DataStore::saveServiceBookings() { + saveRecords(m_serviceBookings, m_serviceBookingCache); } /* @@ -443,6 +577,7 @@ Returns: */ void DataStore::saveJobCards() { + saveRecords(m_jobCards, m_jobCardCache); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 3950b11..cbfd5b3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -11,6 +11,7 @@ Date: 19-May-2026 #include "Map.h" #include "MappingInfo.h" #include "TrackedRecord.h" +#include "SerializedRecords.h" #include "SharedMemory.h" class User; class Notification; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h new file mode 100644 index 0000000..2e04eb0 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h @@ -0,0 +1,28 @@ +/* +File: DataStoreLockGuard.h +Description: Defines the DataStoreLockGuard class used to manage DataStore + locking and unlocking automatically within a scope. +Author: Trenser +Date: 12-June-2026 +*/ + +#pragma once +#include "DataStore.h" + +class DataStoreLockGuard +{ +public: + explicit DataStoreLockGuard(DataStore& dataStore) + : m_dataStore(dataStore) + { + m_dataStore.lockDataStore(); + } + ~DataStoreLockGuard() + { + m_dataStore.unlockDataStore(); + } + DataStoreLockGuard(const DataStoreLockGuard&) = delete; + DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete; +private: + DataStore& m_dataStore; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 1a9f753..f372bef 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -25,6 +25,7 @@ Date:19-May-2026 #include "Timestamp.h" #include "User.h" #include "UserManagementService.h" +#include "DataStoreLockGuard.h" #include "Utility.h" /* @@ -46,18 +47,19 @@ void ServiceManagementService::purchaseService(const util::Vector& { throw std::runtime_error("No user is currently logged in!"); } - auto& servicesMap = m_dataStore.getServices(); - auto& serviceBookingMap = m_dataStore.getServiceBookings(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedServicesMap = m_dataStore.getServices(); + auto& trackedServiceBookingMap = m_dataStore.getServiceBookings(); util::Map selectedServices; int selectedServicesCount = serviceIDs.getSize(); for (int index = 0; index < selectedServicesCount; index++) { - int serviceIndex = servicesMap.find(serviceIDs[index]); + int serviceIndex = trackedServicesMap.find(serviceIDs[index]); if (serviceIndex == -1) { throw std::runtime_error("Service not found!"); } - Service* service = servicesMap.getValueAt(serviceIndex); + Service* service = trackedServicesMap.getValueAt(serviceIndex).data; selectedServices[service->getId()] = service; } ServiceBooking* serviceBooking = Factory::getObject(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0); @@ -65,7 +67,7 @@ void ServiceManagementService::purchaseService(const util::Vector& { throw std::runtime_error("Failed to create service booking"); } - serviceBookingMap[serviceBooking->getId()] = serviceBooking; + trackedServiceBookingMap[serviceBooking->getId()] = util::createNewRecord(serviceBooking); std::string title = "Service Booking succeeded"; std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId(); sendNotification(authenticatedUser, title, message); @@ -90,21 +92,22 @@ void ServiceManagementService::purchaseComboPackage(const std::string& comboPack { throw std::runtime_error("No user is currently logged in!"); } - auto& comboPackagesMap = m_dataStore.getComboPackages(); - auto& serviceBookingMap = m_dataStore.getServiceBookings(); - int comboPackageIndex = comboPackagesMap.find(comboPackageID); + DataStoreLockGuard lock(m_dataStore); + auto& trackedComboPackagesMap = m_dataStore.getComboPackages(); + auto& trackedServiceBookingMap = m_dataStore.getServiceBookings(); + int comboPackageIndex = trackedComboPackagesMap.find(comboPackageID); if (comboPackageIndex == -1) { throw std::runtime_error("Combo Package not found!"); } - const ComboPackage* comboPackage = comboPackagesMap[comboPackageID]; + const ComboPackage* comboPackage = trackedComboPackagesMap[comboPackageID].data; util::Map selectedServices = comboPackage->getServices(); ServiceBooking* serviceBooking = Factory::getObject(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage()); if (serviceBooking == nullptr) { throw std::runtime_error("Failed to create combo package service booking"); } - serviceBookingMap[serviceBooking->getId()] = serviceBooking; + trackedServiceBookingMap[serviceBooking->getId()] = util::createNewRecord(serviceBooking); 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); @@ -514,7 +517,7 @@ Description: Restores inventory quantities for all required items in the service Parameter: ServiceBooking* booking - Pointer to the booking whose inventory items need to be restored Return type: void */ -static void restoreInventory(ServiceBooking* booking) +static void restoreInventory(ServiceBooking* booking, util::Map>& trackedInventoryItems) { const int INCREMENT_VALUE = 1; if (!booking) @@ -533,9 +536,17 @@ static void restoreInventory(ServiceBooking* booking) for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++InventoryIterator) { InventoryItem* item = items.getValueAt(InventoryIterator); + const std::string& currentItemId = item->getId(); + int itemIndex = trackedInventoryItems.find(currentItemId); + if (itemIndex == -1) + { + continue; + } + auto& currentTrackedInventoryItem = trackedInventoryItems.getValueAt(itemIndex); if (item) { item->setQuantity(item->getQuantity() + INCREMENT_VALUE); + currentTrackedInventoryItem.state = RecordState::MODIFIED; } } } @@ -553,23 +564,28 @@ Parameters: util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN) Return type: void */ -static void processBookingCancellation(ServiceBooking* booking, - util::Map& jobs, +static void processBookingCancellation(TrackedRecord& trackedBooking, + util::Map>& jobs, ServiceManagementService& currentService, - util::UserType userType) + util::UserType userType, + util::Map>& trackedInventoryItems) { + ServiceBooking* booking = trackedBooking.data; if (!booking) { return; } + const std::string& bookingId = booking->getId(); for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator) { - JobCard* jobCard = jobs.getValueAt(jobIterator); + auto& trackedJobCard = jobs.getValueAt(jobIterator); + JobCard* jobCard = trackedJobCard.data; if (!jobCard || jobCard->getBookingId() != booking->getId() || jobCard->getStatus() == util::ServiceJobStatus::CANCELLED) { continue; } jobCard->setStatus(util::ServiceJobStatus::CANCELLED); + trackedJobCard.state = RecordState::MODIFIED; if (userType == util::UserType::CUSTOMER) { if (User* technician = booking->getAssignedTechnician()) @@ -602,7 +618,8 @@ static void processBookingCancellation(ServiceBooking* booking, } booking->setAssignedTechnician(nullptr); booking->setAssignedTechnicianId(""); - restoreInventory(booking); + trackedBooking.state = RecordState::MODIFIED; + restoreInventory(booking, trackedInventoryItems); } /* @@ -615,22 +632,25 @@ Return type: void */ void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID) { - auto& users = m_dataStore.getUsers(); - int userIndex = users.find(customerID); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsers = m_dataStore.getUsers(); + int userIndex = trackedUsers.find(customerID); if (userIndex == -1) { throw std::runtime_error("User not found: " + customerID); } - User* customer = users.getValueAt(userIndex); + User* customer = trackedUsers.getValueAt(userIndex).data; if (!customer) { throw std::runtime_error("User not found: " + customerID); } - auto& bookings = m_dataStore.getServiceBookings(); - auto& jobs = m_dataStore.getJobCards(); - for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++) + auto& trackedBookings = m_dataStore.getServiceBookings(); + auto& trackedJobs = m_dataStore.getJobCards(); + auto& trackedInventoryItems = m_dataStore.getInventoryItems(); + for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++) { - ServiceBooking* booking = bookings.getValueAt(iteratorOne); + auto& trackedBooking = trackedBookings.getValueAt(iteratorOne); + ServiceBooking* booking = trackedBooking.data; if (!booking) { continue; @@ -645,8 +665,12 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string& { continue; } - processBookingCancellation(booking, jobs, *this, util::UserType::CUSTOMER); + processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::CUSTOMER, trackedInventoryItems); } + m_dataStore.saveUsers(); + m_dataStore.saveServiceBookings(); + m_dataStore.saveJobCards(); + m_dataStore.saveInventoryItems(); } /* @@ -658,22 +682,25 @@ Return type: void */ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID) { - auto& users = m_dataStore.getUsers(); - int userIndex = users.find(technicianID); + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsers = m_dataStore.getUsers(); + int userIndex = trackedUsers.find(technicianID); if (userIndex == -1) { throw std::runtime_error("User not found: " + technicianID); } - User* technician = users.getValueAt(userIndex); + User* technician = trackedUsers.getValueAt(userIndex).data; 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++) + auto& trackedBookings = m_dataStore.getServiceBookings(); + auto& trackedJobs = m_dataStore.getJobCards(); + auto& trackedInventoryItems = m_dataStore.getInventoryItems(); + for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++) { - ServiceBooking* booking = bookings.getValueAt(iteratorOne); + auto& trackedBooking = trackedBookings.getValueAt(iteratorOne); + ServiceBooking* booking = trackedBooking.data; if (!booking) { continue; @@ -694,8 +721,12 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia { continue; } - processBookingCancellation(booking, jobs, *this, util::UserType::TECHNICIAN); + processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::TECHNICIAN, trackedInventoryItems); } + m_dataStore.saveUsers(); + m_dataStore.saveInventoryItems(); + m_dataStore.saveServiceBookings(); + m_dataStore.saveJobCards(); } /* @@ -709,6 +740,7 @@ Return type: void */ void ServiceManagementService::createComboPackage(const std::string& packageName, const util::Vector& serviceIDsInNewCombo, double discountPercentage) { + DataStoreLockGuard lock(m_dataStore); if (packageName.empty()) { throw std::invalid_argument("The Combo Package Name cannot be empty.\n"); @@ -721,19 +753,19 @@ void ServiceManagementService::createComboPackage(const std::string& packageName { throw std::invalid_argument("Discount percentage must be between 0 and 100."); } - auto& servicesMap = m_dataStore.getServices(); + auto& trackedServicesMap = m_dataStore.getServices(); for (int index = 0; index < serviceIDsInNewCombo.getSize(); index++) { - const std::string serviceid = serviceIDsInNewCombo[index]; - if (servicesMap.find(serviceid) == -1) + const std::string& serviceid = serviceIDsInNewCombo[index]; + if (trackedServicesMap.find(serviceid) == -1) { throw std::runtime_error("Service ID not found: " + serviceid); } } - auto& comboPackageMap = m_dataStore.getComboPackages(); - for (int iterator = 0; iterator < comboPackageMap.getSize(); iterator++) + auto& trackedComboPackageMap = m_dataStore.getComboPackages(); + for (int iterator = 0; iterator < trackedComboPackageMap.getSize(); iterator++) { - ComboPackage* existingCombos = comboPackageMap.getValueAt(iterator); + ComboPackage* existingCombos = trackedComboPackageMap.getValueAt(iterator).data; const util::Map& servicesInsideExistingCombos = existingCombos->getServices(); if (servicesInsideExistingCombos.getSize() == serviceIDsInNewCombo.getSize()) { @@ -757,15 +789,16 @@ void ServiceManagementService::createComboPackage(const std::string& packageName for (int iteratorOne = 0; iteratorOne < serviceIDsInNewCombo.getSize(); iteratorOne++) { const std::string& serviceId = serviceIDsInNewCombo[iteratorOne]; - int serviceIndex = servicesMap.find(serviceId); + int serviceIndex = trackedServicesMap.find(serviceId); if (serviceIndex == -1) { throw std::runtime_error("Service ID not found: " + serviceId); } - selectedServices.insert(serviceId, servicesMap.getValueAt(serviceIndex)); + selectedServices.insert(serviceId, trackedServicesMap.getValueAt(serviceIndex).data); } ComboPackage* newComboPackage = Factory::getObject(packageName, discountPercentage, selectedServices); - comboPackageMap.insert(newComboPackage->getId(), newComboPackage); + trackedComboPackageMap.insert(newComboPackage->getId(), util::createNewRecord(newComboPackage)); + m_dataStore.saveComboPackages(); } /* @@ -776,7 +809,10 @@ Return type: util::Map */ util::Map ServiceManagementService::getComboPackages() { - return m_dataStore.getComboPackages(); + DataStoreLockGuard lock(m_dataStore); + util::Map comboPackages; + comboPackages = util::getObjects(m_dataStore.getComboPackages()); + return comboPackages; } /* @@ -787,14 +823,17 @@ Return type: void */ void ServiceManagementService::removeComboPackage(const std::string& comboPackageID) { + DataStoreLockGuard lock(m_dataStore); bool removed = false; - util::Map& currentComboPackages = m_dataStore.getComboPackages(); - for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++) + auto& trackedComboPackages = m_dataStore.getComboPackages(); + for (int iterator = 0; iterator < trackedComboPackages.getSize(); iterator++) { - ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator); + auto& comboPackage = trackedComboPackages.getValueAt(iterator); + ComboPackage* currentComboPackage = comboPackage.data; if (currentComboPackage && currentComboPackage->getId() == comboPackageID) { currentComboPackage->setState(util::State::INACTIVE); + comboPackage.state = RecordState::MODIFIED; removed = true; break; } @@ -803,6 +842,7 @@ void ServiceManagementService::removeComboPackage(const std::string& comboPackag { throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found."); } + m_dataStore.saveComboPackages(); } /* @@ -815,7 +855,9 @@ Returns: */ util::Map ServiceManagementService::getServiceBookings() { - return m_dataStore.getServiceBookings(); + DataStoreLockGuard lock(m_dataStore); + util::Map serviceBookings; + serviceBookings = util::getObjects(m_dataStore.getServiceBookings()); } /* @@ -856,7 +898,8 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const { UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); - auto& currentJobCards = m_dataStore.getJobCards(); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedJobCards = m_dataStore.getJobCards(); if (currentBooking == nullptr) { throw std::runtime_error("Service Booking not available"); @@ -902,7 +945,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const JobCard* jobCard = Factory::getObject(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp()); if (jobCard) { - currentJobCards.insert(jobCard->getId(), jobCard); + currentTrackedJobCards.insert(jobCard->getId(), util::createNewRecord(jobCard)); sendNotification(selectedTechnician, title, message); } else @@ -930,14 +973,15 @@ Throws: void ServiceManagementService::createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost) { util::Map currentServiceInventoryItems; - auto inventoryItems = m_dataStore.getInventoryItems(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItems = m_dataStore.getInventoryItems(); for (int iteratorOne =0; iteratorOne < inventoryItemIDs.getSize(); iteratorOne++) { std::string currentItemID = inventoryItemIDs[iteratorOne]; bool itemFound = false; - for (int iteratorTwo = 0; iteratorTwo < inventoryItems.getSize(); iteratorTwo++) + for (int iteratorTwo = 0; iteratorTwo < trackedInventoryItems.getSize(); iteratorTwo++) { - InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iteratorTwo); + InventoryItem* currentInventoryItem = trackedInventoryItems.getValueAt(iteratorTwo).data; if (currentInventoryItem && currentInventoryItem->getId() == currentItemID) { itemFound = true; @@ -955,12 +999,12 @@ void ServiceManagementService::createService(const std::string& name, const util { throw std::runtime_error("Unable to create new service."); } - util::Map& currentServices = m_dataStore.getServices(); + auto& currentServices = m_dataStore.getServices(); if (currentServices.find(newService->getId()) != -1) { throw std::runtime_error("Service with this ID Already exists."); } - currentServices.insert(newService->getId(), newService); + currentServices.insert(newService->getId(), util::createNewRecord(newService)); } /* @@ -973,7 +1017,10 @@ Returns: */ util::Map ServiceManagementService::getServices() { - return m_dataStore.getServices(); + DataStoreLockGuard lock(m_dataStore); + util::Map services; + services = util::getObjects(m_dataStore.getServices()); + return services; } /* @@ -988,14 +1035,19 @@ Throws: */ void ServiceManagementService::removeService(const std::string& serviceID) { - util::Map& currentServices = m_dataStore.getServices(); - util::Map& currentComboPackages = m_dataStore.getComboPackages(); - if (currentServices.find(serviceID) != -1) + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedServices = m_dataStore.getServices(); + auto& currentTrackedComboPackages = m_dataStore.getComboPackages(); + if (currentTrackedServices.find(serviceID) != -1) { - currentServices.getValueAt(currentServices.find(serviceID))->setState(util::State::INACTIVE); - for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++) + int serviceIndex, comboPackageIndex; + serviceIndex = currentTrackedServices.find(serviceID); + currentTrackedServices.getValueAt(serviceIndex).data->setState(util::State::INACTIVE); + currentTrackedServices.getValueAt(serviceIndex).state = RecordState::MODIFIED; + for (int iterator = 0; iterator < currentTrackedComboPackages.getSize(); iterator++) { - ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator); + comboPackageIndex = iterator; + ComboPackage* currentComboPackage = currentTrackedComboPackages.getValueAt(iterator).data; if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE) { util::Map currentServices = currentComboPackage->getServices(); @@ -1005,6 +1057,7 @@ void ServiceManagementService::removeService(const std::string& serviceID) if (currentService->getId() == serviceID) { currentComboPackage->setState(util::State::INACTIVE); + currentTrackedComboPackages.getValueAt(comboPackageIndex).state = RecordState::MODIFIED; break; } } @@ -1015,6 +1068,8 @@ void ServiceManagementService::removeService(const std::string& serviceID) { throw std::runtime_error("Service not found."); } + m_dataStore.saveServices(); + m_dataStore.saveComboPackages(); } /* @@ -1053,11 +1108,12 @@ Returns: */ util::Map ServiceManagementService::getJobCards(const std::string& technicianID) { - util::Map jobCards = m_dataStore.getJobCards(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedJobCards = m_dataStore.getJobCards(); util::Map technicianJobCards; - for (int iterator = 0; iterator < jobCards.getSize(); iterator++) + for (int iterator = 0; iterator < trackedJobCards.getSize(); iterator++) { - JobCard* currentJobCard = jobCards.getValueAt(iterator); + JobCard* currentJobCard = trackedJobCards.getValueAt(iterator).data; if (currentJobCard->getTechnicianId() == technicianID) { technicianJobCards.insert(currentJobCard->getId(), currentJobCard); @@ -1106,6 +1162,7 @@ Returns: */ void ServiceManagementService::updateJobStatus(const std::string& jobID) { + DataStoreLockGuard lock(m_dataStore); AuthenticationManagementService authenticationManagementService; PaymentManagementService paymentManagementService; bool jobStatusUpdated = false, serviceBookingCompleted; @@ -1120,8 +1177,15 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID) { 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) { + 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) { @@ -1130,16 +1194,20 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID) if (currentJob->getStatus() == util::ServiceJobStatus::STARTED) { currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS); + trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED; jobStatusUpdated = true; } else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS) { currentJob->setStatus(util::ServiceJobStatus::COMPLETED); + trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED; jobStatusUpdated = true; serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); if (serviceBookingCompleted) { + const std::string& bookingId = currentJob->getBookingId(); currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED); + trackedServiceBookings.getValueAt(trackedServiceBookings.find(bookingId)).state = RecordState::MODIFIED; paymentManagementService.generateInvoice(currentJob->getBooking()); std::string title = "Service Booking completed. Invoice Generated."; std::string message = "Services completed for the booking and invoice generated."; @@ -1155,4 +1223,6 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID) { 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 From f545d57f798b9ab72f84203cc1ff7c8566a61194 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Mon, 15 Jun 2026 11:09:42 +0530 Subject: [PATCH 10/39] Implement Model Refactoring 1949: Model Refactoring UserStory #1949 Replaced CSV-based serialization and deserialization in the Invoice model with fixed-size SerializedRecord structures for shared memory storage. Implemented serialize() method to convert Invoice objects into SerializedInvoice records using direct struct field assignment and strcpy_s for string fields. Implemented deserialize() method to reconstruct Invoice objects directly from SerializedInvoice types instead of parsing CSV strings. Updated Invoice class interfaces to use SerializedInvoice types, removing legacy CSV serialization APIs including getHeaders() function. Added SerializedRecords.h dependency and forward declarations for SerializedInvoice structure in Invoice header. Minor formatting adjustments in DataStore.cpp (closing brace placement). N/A Sreeja Reghukumar, please review --- .../datastores/DataStore.cpp | 3 +- .../models/Invoice.cpp | 130 ++++++------------ .../models/Invoice.h | 21 ++- 3 files changed, 52 insertions(+), 102 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 1cade32..af6fdc6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -561,5 +561,4 @@ bool DataStore::unlockDataStore() return false; } return ReleaseMutex(m_globalMutex) != 0; -} - +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.cpp index f24a013..7788b84 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.cpp @@ -9,6 +9,7 @@ Date: 19-May-2026 #include #include +#include "SerializedRecords.h" #include "Invoice.h" #include "Factory.h" #include "InventoryItem.h" @@ -34,7 +35,8 @@ Invoice::Invoice() m_discountPercentage(0.0), m_totalAmount(0.0), m_paymentMethod(util::PaymentMode()), - m_status(util::PaymentStatus()) {} + m_status(util::PaymentStatus()) { +} /* Function: Invoice @@ -57,16 +59,16 @@ Returns: Invoice::Invoice( const std::string& bookingId, ServiceBooking* booking, - const util::Timestamp& invoiceDate, - double laborCost, + const util::Timestamp& invoiceDate, + double laborCost, const util::Map& parts, double partsCost, - double discountPercentage, - double totalAmount, - const util::Timestamp& paymentDate, - util::PaymentMode paymentMethod, + double discountPercentage, + double totalAmount, + const util::Timestamp& paymentDate, + util::PaymentMode paymentMethod, util::PaymentStatus status - ) +) : m_id("INV" + std::to_string(++m_uid)), m_bookingId(bookingId), m_booking(booking), @@ -78,7 +80,7 @@ Invoice::Invoice( m_totalAmount(totalAmount), m_paymentDate(paymentDate), m_paymentMethod(paymentMethod), - m_status(status) + m_status(status) { int numberOfParts = m_parts.getSize(); auto partPointers = m_parts.getValues(); @@ -473,100 +475,50 @@ static util::Vector getPartIDsAsVector(const std::string& partIDsSt /* Function: serialize -Description: Serializes the invoice into a CSV-formatted string. +Description: Serializes the Invoice object into a SerializedInvoice record. Parameters: - None Returns: - - std::string: Serialized invoice record + - SerializedInvoice: Serialized representation of the invoice */ -std::string Invoice::serialize() const +SerializedInvoice Invoice::serialize() const { - std::ostringstream serializedInvoice; - serializedInvoice << m_id << ',' - << m_bookingId << ',' - << m_invoiceDate.toString() << ',' - << m_laborCost << ',' - << getPartIDsAsString(m_partIDs) << ',' - << m_partsCost << ',' - << m_discountPercentage << ',' - << m_totalAmount << ',' - << m_paymentDate.toString() << ',' - << util::getPaymentModeString(m_paymentMethod) << ',' - << util::getPaymentStatusString(m_status); - return serializedInvoice.str(); + SerializedInvoice serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.bookingId, sizeof(serialized.bookingId), m_bookingId.c_str()); + strcpy_s(serialized.partIDs, sizeof(serialized.partIDs), getPartIDsAsString(m_partIDs).c_str()); + serialized.invoiceDate = m_invoiceDate; + serialized.laborCost = m_laborCost; + serialized.partsCost = m_partsCost; + serialized.discountPercentage = m_discountPercentage; + serialized.totalAmount = m_totalAmount; + serialized.paymentDate = m_paymentDate; + serialized.paymentMethod = m_paymentMethod; + serialized.status = m_status; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into an Invoice object. +Description: Deserializes a SerializedInvoice record into an Invoice object. Parameters: - - record: const std::string&, serialized invoice record + - serializedInvoice: const SerializedInvoice&, serialized invoice record Returns: - Invoice*: Pointer to the deserialized Invoice object -Throws: - - std::runtime_error if data is invalid */ -Invoice* Invoice::deserialize(const std::string& record) +Invoice* Invoice::deserialize(const SerializedInvoice& serializedInvoice) { - std::string id, bookingId; - std::string invoiceDateString, laborCostString, partIDsString; - std::string partsCostString, discountPercentageString, totalAmountString; - std::string paymentDateString, paymentMethodString, statusString; - double laborCost, partsCost, discountPercentage, totalAmount; - std::istringstream serializedInvoice(record); - getline(serializedInvoice, id, ','); - getline(serializedInvoice, bookingId, ','); - getline(serializedInvoice, invoiceDateString, ','); - getline(serializedInvoice, laborCostString, ','); - getline(serializedInvoice, partIDsString, ','); - getline(serializedInvoice, partsCostString, ','); - getline(serializedInvoice, discountPercentageString, ','); - getline(serializedInvoice, totalAmountString, ','); - getline(serializedInvoice, paymentDateString, ','); - getline(serializedInvoice, paymentMethodString, ','); - getline(serializedInvoice, statusString, ','); - util::Timestamp invoiceDate; - util::Timestamp paymentDate; - try - { - invoiceDate = util::Timestamp::fromString(invoiceDateString); - paymentDate = util::Timestamp::fromString(paymentDateString); - laborCost = std::stod(laborCostString); - partsCost = std::stod(partsCostString); - discountPercentage = std::stod(discountPercentageString); - totalAmount = std::stod(totalAmountString); - } - catch (...) - { - throw std::runtime_error("Invalid invoice data"); - } - util::Vector partIDs = getPartIDsAsVector(partIDsString); - util::PaymentMode paymentMethod = util::getPaymentMode(paymentMethodString); - util::PaymentStatus status = util::getPaymentStatus(statusString); + util::Vector partIDs = getPartIDsAsVector(serializedInvoice.partIDs); return Factory::getObject( - id, - bookingId, - invoiceDate, + serializedInvoice.id, + serializedInvoice.bookingId, + serializedInvoice.invoiceDate, partIDs, - laborCost, - partsCost, - discountPercentage, - totalAmount, - paymentDate, - paymentMethod, - status - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for invoice serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,BookingID,InvoiceDate,LaborCost,PartIDs,PartsCost,DiscountPercentage,TotalAmount,PaymentDate,PaymentMethod,Status") -*/ -std::string Invoice::getHeaders() -{ - return "ID,BookingID,InvoiceDate,LaborCost,PartIDs,PartsCost,DiscountPercentage,TotalAmount,PaymentDate,PaymentMethod,Status"; + serializedInvoice.laborCost, + serializedInvoice.partsCost, + serializedInvoice.discountPercentage, + serializedInvoice.totalAmount, + serializedInvoice.paymentDate, + serializedInvoice.paymentMethod, + serializedInvoice.status); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.h index 3374de9..11343e7 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Invoice.h @@ -6,7 +6,6 @@ Author: Trenser Date: 19-May-2026 */ - #pragma once #include #include "Map.h" @@ -16,6 +15,7 @@ Date: 19-May-2026 class ServiceBooking; class InventoryItem; +struct SerializedInvoice; class Invoice { @@ -39,14 +39,14 @@ public: Invoice( const std::string& bookingId, ServiceBooking* booking, - const util::Timestamp& invoiceDate, - double laborCost, - const util::Map& parts, + const util::Timestamp& invoiceDate, + double laborCost, + const util::Map& parts, double partsCost, - double discountPercentage, - double totalAmount, - const util::Timestamp& paymentDate, - util::PaymentMode paymentMethod, + double discountPercentage, + double totalAmount, + const util::Timestamp& paymentDate, + util::PaymentMode paymentMethod, util::PaymentStatus status ); Invoice( @@ -87,7 +87,6 @@ public: void setPaymentDate(const util::Timestamp& paymentDate); void setPaymentMethod(util::PaymentMode paymentMethod); void setStatus(util::PaymentStatus status); - std::string serialize() const; - static Invoice* deserialize(const std::string&); - static std::string getHeaders(); + SerializedInvoice serialize() const; + static Invoice* deserialize(const SerializedInvoice&); }; \ No newline at end of file From 7047e96b3cc6120496a149d6f77225516e3616b0 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Mon, 15 Jun 2026 12:43:44 +0530 Subject: [PATCH 11/39] Implement Service Refactoring 1950: Implement Service Refactorings UserStory #1950 1. Added `DataStoreLockGuard` integration in `PaymentManagementService` methods (`sendPaymentReminders`, `generateInvoice`, `getInvoices`, `completePayment`, `getAllInvoices`, `confirmPayment`) to ensure thread-safe access to the datastore. 2. Implemented record enrichment logic in `DataStore::getInvoices()` to automatically link `Invoice` objects with their corresponding `ServiceBooking` and `InventoryItem` entities during loading, validating relationships and throwing exceptions for invalid references. 3. Refactored invoice persistence by implementing `saveInvoices()` using `saveRecords` to write tracked records directly to shared memory. 4. Updated invoice creation and modification flows: - `generateInvoice`: Uses `createNewRecord` to insert new invoices into the tracked cache and triggers immediate persistence. - `completePayment` & `confirmPayment`: Marks records as `MODIFIED` in the `TrackedRecord` state before saving changes. 5. Updated data access patterns across `PaymentManagementService` to use `.data` pointers from `TrackedRecord` objects returned by the datastore, replacing direct pointer access to mapped objects. 6. Added necessary header dependencies including `DataStoreLockGuard.h`, `Invoice.h`, and `SerializedRecords.h` to support locking mechanisms and structured serialization. N/A Sreeja Reghukumar, please review --- .../Trenser.VehicleServiceSystem.vcxproj | 1 + .../datastores/DataStore.cpp | 41 +++++++++++++++ .../datastores/DataStoreLockGuard.h | 28 ++++++++++ .../services/PaymentManagementService.cpp | 52 ++++++++++++------- 4 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 6a269f5..7c4bfd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -157,6 +157,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index af6fdc6..3092551 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -12,6 +12,7 @@ Date: 19-May-2026 #include "Config.h" #include "SerializedRecords.h" #include "FileHelper.h" +#include "Invoice.h" /* Function: DataStore @@ -319,6 +320,45 @@ Returns: */ util::Map>& DataStore::getInvoices() { + auto& serviceBookings = getServiceBookings(); + auto& inventoryItems = getInventoryItems(); + util::Map> invoices = loadRecords(m_invoices); + refreshCache(m_invoiceCache, invoices); + for (int iterator = 0; iterator < m_invoiceCache.getSize(); iterator++) + { + auto& trackedInvoice = m_invoiceCache.getValueAt(iterator); + Invoice* invoice = trackedInvoice.data; + if (!invoice) + { + continue; + } + const std::string& currentBookingId = invoice->getBookingId(); + int currentBookingIndex = serviceBookings.find(currentBookingId); + if (currentBookingIndex == -1) + { + throw std::runtime_error("Invalid Service Booking Index."); + } + ServiceBooking* currentBooking = serviceBookings.getValueAt(currentBookingIndex).data; + auto& currentInventoryItemIds = invoice->getPartIDs(); + util::Map currentInventoryItems; + for (int iterator = 0; iterator < currentInventoryItemIds.getSize(); iterator++) + { + const std::string& currentItemId = currentInventoryItemIds[iterator]; + int currentItemIndex = inventoryItems.find(currentItemId); + if (currentItemIndex == -1) + { + throw std::runtime_error("Invalid Inventory item id."); + } + InventoryItem* currentItem = inventoryItems.getValueAt(currentItemIndex).data; + if (!currentItem) + { + continue; + } + currentInventoryItems[currentItemId] = currentItem; + } + invoice->setBooking(currentBooking); + invoice->setParts(currentInventoryItems); + } return m_invoiceCache; } @@ -455,6 +495,7 @@ Returns: */ void DataStore::saveInvoices() { + saveRecords(m_invoices, m_invoiceCache); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h new file mode 100644 index 0000000..2e04eb0 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h @@ -0,0 +1,28 @@ +/* +File: DataStoreLockGuard.h +Description: Defines the DataStoreLockGuard class used to manage DataStore + locking and unlocking automatically within a scope. +Author: Trenser +Date: 12-June-2026 +*/ + +#pragma once +#include "DataStore.h" + +class DataStoreLockGuard +{ +public: + explicit DataStoreLockGuard(DataStore& dataStore) + : m_dataStore(dataStore) + { + m_dataStore.lockDataStore(); + } + ~DataStoreLockGuard() + { + m_dataStore.unlockDataStore(); + } + DataStoreLockGuard(const DataStoreLockGuard&) = delete; + DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete; +private: + DataStore& m_dataStore; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index 1fc1ff8..45f1742 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -16,6 +16,7 @@ Date: 20-May-2026 #include "Invoice.h" #include "JobCard.h" #include "PaymentManagementService.h" +#include "DataStoreLockGuard.h" #include "Service.h" #include "ServiceBooking.h" #include "Timestamp.h" @@ -113,11 +114,12 @@ Returns: */ void PaymentManagementService::sendPaymentReminders() { - auto& invoicesMap = m_dataStore.getInvoices(); - int invoicesMapSize = invoicesMap.getSize(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedInvoicesMap = m_dataStore.getInvoices(); + int invoicesMapSize = trackedInvoicesMap.getSize(); for (int index = 0; index < invoicesMapSize; index++) { - const Invoice* invoice = invoicesMap.getValueAt(index); + const Invoice* invoice = trackedInvoicesMap.getValueAt(index).data; if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING) { util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate(); @@ -290,6 +292,7 @@ Throws: */ void PaymentManagementService::generateInvoice(ServiceBooking* booking) { + DataStoreLockGuard lock(m_dataStore); if (!booking) { throw std::runtime_error("Invoice generation failed: booking is null."); @@ -299,10 +302,10 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking) std::string bookingID = booking->getId(); util::Map servicesInTheBookedService = booking->getServices(); util::Map completeInventoryItemMapOfBooking; - util::Map currentJobCards = m_dataStore.getJobCards(); - for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++) + auto& currentTrackedJobCards = m_dataStore.getJobCards(); + for (int iterator = 0; iterator < currentTrackedJobCards.getSize(); iterator++) { - JobCard* currentJobCard = currentJobCards.getValueAt(iterator); + JobCard* currentJobCard = currentTrackedJobCards.getValueAt(iterator).data; util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus(); if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED) { @@ -322,8 +325,9 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking) totalServiceCost = totalLaborCost + totalPartsCost; totalServiceCost -= (totalServiceCost * (discountPercentage / 100)); Invoice* invoice = Factory::getObject(bookingID, booking, util::Timestamp(), totalLaborCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING); - util::Map& currentInvoices = m_dataStore.getInvoices(); - currentInvoices.insert(invoice->getId(), invoice); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); + currentTrackedInvoices.insert(invoice->getId(), util::createNewRecord(invoice)); + m_dataStore.saveInvoices(); } /* @@ -336,11 +340,12 @@ Returns: */ util::Map PaymentManagementService::getInvoices(const std::string& customerID) { - util::Map& currentInvoices = m_dataStore.getInvoices(); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); util::Map currentUserInvoices; - for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++) + for (int iterator = 0; iterator < currentTrackedInvoices.getSize(); iterator++) { - Invoice* currentInvoice = currentInvoices.getValueAt(iterator); + Invoice* currentInvoice = currentTrackedInvoices.getValueAt(iterator).data; if (currentInvoice->getBooking()->getCustomerId() == customerID) { currentUserInvoices.insert(currentInvoice->getId(), currentInvoice); @@ -363,11 +368,13 @@ Throws: */ void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode) { - auto& currentInvoices = m_dataStore.getInvoices(); - int invoiceIndex = currentInvoices.find(invoiceID); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); + int invoiceIndex = currentTrackedInvoices.find(invoiceID); if (invoiceIndex != -1) { - Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); + auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex); + Invoice* invoice = trackedInvoice.data; if (invoice && invoice->getStatus() != util::PaymentStatus::PAID) { User* currentUser = invoice->getBooking()->getCustomer(); @@ -378,12 +385,14 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti title = "Payment successful"; message = "Payment successful for Invoice ID " + invoiceID; sendNotification(currentUser, title, message); + trackedInvoice.state = RecordState::MODIFIED; } } else { throw std::runtime_error("Payment failed: invalid invoice ID."); } + m_dataStore.saveInvoices(); } /* @@ -396,7 +405,10 @@ Returns: */ util::Map& PaymentManagementService::getAllInvoices() { - return m_dataStore.getInvoices(); + DataStoreLockGuard lock(m_dataStore); + util::Map invoices; + invoices = util::getObjects(m_dataStore.getInvoices()); + return invoices; } /* @@ -412,20 +424,24 @@ Throws: */ void PaymentManagementService::confirmPayment(const std::string& invoiceID) { - auto& currentInvoices = m_dataStore.getInvoices(); - int invoiceIndex = currentInvoices.find(invoiceID); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); + int invoiceIndex = currentTrackedInvoices.find(invoiceID); if (invoiceIndex == -1) { throw std::runtime_error("Payment confirmation failed: invalid invoice ID."); } - Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); + auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex); + Invoice* invoice = trackedInvoice.data; if (!invoice || invoice->getStatus() != util::PaymentStatus::PAID) { throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation."); } User* currentUser = invoice->getBooking()->getCustomer(); invoice->setStatus(util::PaymentStatus::COMPLETED); + trackedInvoice.state = RecordState::MODIFIED; std::string title = "Payment Confirmed"; std::string message = "Payment Confirmed for Invoice ID " + invoiceID; sendNotification(currentUser, title, message); + m_dataStore.saveInvoices(); } \ No newline at end of file From d44cc86af0f1010b8240d0d65f94623d00007261 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Mon, 15 Jun 2026 14:43:48 +0530 Subject: [PATCH 12/39] Implement the review fixes --- .../services/AuthenticationManagementService.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp index 27be89f..71d66b6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp @@ -79,15 +79,15 @@ void AuthenticationManagementService::changePassword(const std::string& newPassw { DataStoreLockGuard lock(m_dataStore); auto& trackedUsersMap = m_dataStore.getUsers(); + if (m_authenticatedUser == nullptr) + { + throw std::runtime_error("There is no user currently logged in!"); + } int index = trackedUsersMap.find(m_authenticatedUser->getId()); if (index == -1) { throw std::runtime_error("User does not exist!\n"); } - if (m_authenticatedUser == nullptr) - { - throw std::runtime_error("There is no user currently logged in!"); - } m_authenticatedUser->setPassword(newPassword); trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; m_dataStore.saveUsers(); From b5c8b1ee9bdc97071c4d92cefc68e852c881c20f Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Mon, 15 Jun 2026 14:46:37 +0530 Subject: [PATCH 13/39] Implement review fixes --- .../services/UserManagementService.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 3eeead8..9b1c61c 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -177,7 +177,6 @@ util::Vector UserManagementService::getUserNotifications(const st { notificationsVector.push_back(notifications.getValueAt(index)); } - m_dataStore.unlockDataStore(); return notificationsVector; } else From e1676568cdd47c15f6d1cfa758ef296a1fffa32c Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Mon, 15 Jun 2026 14:52:19 +0530 Subject: [PATCH 14/39] Implement review fixes --- .../services/ServiceManagementService.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index f372bef..8f95e02 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -858,6 +858,7 @@ util::Map ServiceManagementService::getServiceBook DataStoreLockGuard lock(m_dataStore); util::Map serviceBookings; serviceBookings = util::getObjects(m_dataStore.getServiceBookings()); + return serviceBookings; } /* @@ -896,9 +897,9 @@ Throws: */ void ServiceManagementService::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID) { + DataStoreLockGuard lock(m_dataStore); UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); - DataStoreLockGuard lock(m_dataStore); auto& currentTrackedJobCards = m_dataStore.getJobCards(); if (currentBooking == nullptr) { @@ -955,6 +956,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const title = "Technician assigned"; message = "A technician has been assigned to your Service Booking with ID " + bookingID; sendNotification(currentBooking->getCustomer(), title, message); + m_dataStore.saveJobCards(); } /* @@ -972,9 +974,10 @@ Throws: */ void ServiceManagementService::createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost) { - util::Map currentServiceInventoryItems; DataStoreLockGuard lock(m_dataStore); + util::Map currentServiceInventoryItems; auto& trackedInventoryItems = m_dataStore.getInventoryItems(); + auto& currentServices = m_dataStore.getServices(); for (int iteratorOne =0; iteratorOne < inventoryItemIDs.getSize(); iteratorOne++) { std::string currentItemID = inventoryItemIDs[iteratorOne]; @@ -999,12 +1002,12 @@ void ServiceManagementService::createService(const std::string& name, const util { throw std::runtime_error("Unable to create new service."); } - auto& currentServices = m_dataStore.getServices(); if (currentServices.find(newService->getId()) != -1) { throw std::runtime_error("Service with this ID Already exists."); } currentServices.insert(newService->getId(), util::createNewRecord(newService)); + m_dataStore.saveServices(); } /* From d2a7420db5fb78c09d55e6ba4621d2bf07c44b4a Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Mon, 15 Jun 2026 14:59:55 +0530 Subject: [PATCH 15/39] Implement Review Fixes --- .../services/PaymentManagementService.cpp | 4 ++-- .../services/PaymentManagementService.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index 45f1742..dfd9aeb 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -401,9 +401,9 @@ Description: Provides access to all invoices stored in the data store. Parameters: - none Returns: - - util::Map&: Map of invoice IDs to invoice objects + - util::Map: Map of invoice IDs to invoice objects */ -util::Map& PaymentManagementService::getAllInvoices() +util::Map PaymentManagementService::getAllInvoices() { DataStoreLockGuard lock(m_dataStore); util::Map invoices; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h index 8961a2d..04c5560 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h @@ -28,7 +28,7 @@ public: void generateInvoice(ServiceBooking* booking); util::Map getInvoices(const std::string& customerID); void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode); - util::Map& getAllInvoices(); + util::Map getAllInvoices(); void confirmPayment(const std::string& invoiceID); void sendPaymentReminders(); void sendNotification(User* user, const std::string& title, const std::string& message) override; From 5f4ee72ffef2ddd1f168359bfd3f072c4799e529 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Fri, 12 Jun 2026 17:36:15 +0530 Subject: [PATCH 16/39] Implement Notification Model Refactoring 1953: Model Refactoring UserStory #1953 1. Replaced CSV-based Notification serialization and deserialization with SerializedNotification record-based serialization for shared memory storage. 2. Implemented Notification::serialize() to convert Notification objects into fixed-size SerializedNotification structures. 3. Implemented Notification::deserialize() to reconstruct Notification objects directly from SerializedNotification records. 4. Added Notification state persistence by introducing util::State support in constructors, serialization, and deserialization flows. 5. Updated Notification class interfaces to use SerializedNotification types instead of std::string serialization APIs. 6. Removed legacy CSV serialization support, including CSV parsing logic and header generation functionality. 7. Added SerializedNotification dependencies through SerializedRecords.h inclusion and forward declaration support. 8. Initialized Notification objects with ACTIVE state by default and added state getter/setter APIs. N/A Sreeja Reghukumar, please review --- .../models/Notification.cpp | 109 +++++++++--------- .../models/Notification.h | 12 +- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp index 0bae917..ad1b416 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp @@ -7,6 +7,7 @@ Date: 19-May-2026 */ #include +#include "SerializedRecords.h" #include "Notification.h" #include "StringHelper.h" #include "Factory.h" @@ -23,7 +24,8 @@ Returns: */ Notification::Notification() : m_id("NOT" + std::to_string(++m_uid)), - m_recipient(nullptr) {} + m_recipient(nullptr), m_state(util::State::ACTIVE) { +} /* Function: Notification @@ -43,7 +45,9 @@ Notification::Notification(const std::string& recipientUserId, User* recipient, m_recipient(recipient), m_title(title), m_message(message), - m_createdAt(createdAt) {} + m_state(util::State::ACTIVE), + m_createdAt(createdAt) { +} /* Function: Notification (parameterized constructor with ID) @@ -58,13 +62,14 @@ Parameters: Returns: - A new Notification object */ -Notification::Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt) +Notification::Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt, const util::State& state) : m_id(id), m_recipientUserId(recipientUserId), m_recipient(nullptr), m_title(title), m_message(message), - m_createdAt(createdAt) + m_createdAt(createdAt), + m_state(state) { int idNumber = util::extractNumber(m_id); if (idNumber > m_uid) @@ -79,7 +84,7 @@ Description: Retrieves the unique ID of the notification. Returns: - const std::string& representing the notification ID. */ -const std::string& Notification::getId() const +const std::string& Notification::getId() const { return m_id; } @@ -139,6 +144,17 @@ const util::Timestamp& Notification::getCreatedAt() const return m_createdAt; } +/* +Function: getState +Description: Retrieves the Notification state +Returns: + - const util::Timestamp& representing the creation timestamp. +*/ +util::State Notification::getState() const +{ + return m_state; +} + /* Function: setId Description: Sets the unique ID of the notification. @@ -217,71 +233,54 @@ void Notification::setCreatedAt(const util::Timestamp& createdAt) m_createdAt = createdAt; } +/* +Function: setState +Description: Sets the Notification state. +Parameters: + - state: Notification state value. +Returns: + - void +*/ +void Notification::setState(util::State state) +{ + m_state = state; +} + /* Function: serialize -Description: Serializes the notification into a CSV-formatted string. +Description: Serializes the Notification object into a SerializedNotification record. Parameters: - None Returns: - - std::string: Serialized notification record + - SerializedNotification: Serialized representation of the notification */ -std::string Notification::serialize() const +SerializedNotification Notification::serialize() const { - std::ostringstream serializedNotification; - serializedNotification << m_id << ',' - << m_recipientUserId << ',' - << m_title << ',' - << m_message << ',' - << m_createdAt.toString(); - return serializedNotification.str(); + SerializedNotification serialized = {}; + strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); + strcpy_s(serialized.recipientUserId, sizeof(serialized.recipientUserId), m_recipientUserId.c_str()); + strcpy_s(serialized.title, sizeof(serialized.title), m_title.c_str()); + strcpy_s(serialized.message, sizeof(serialized.message), m_message.c_str()); + serialized.createdAt = m_createdAt; + serialized.state = m_state; + return serialized; } /* Function: deserialize -Description: Deserializes a CSV-formatted string into a Notification object. +Description: Deserializes a SerializedNotification record into a Notification object. Parameters: - - record: const std::string&, serialized notification record + - serializedNotification: const SerializedNotification&, serialized notification record Returns: - Notification*: Pointer to the deserialized Notification object -Throws: - - std::runtime_error if timestamp parsing fails */ -Notification* Notification::deserialize(const std::string& record) +Notification* Notification::deserialize(const SerializedNotification& serializedNotification) { - std::string id, recipientUserId, title, message, createdAtTimestampString; - std::istringstream serializedNotification(record); - getline(serializedNotification, id, ','); - getline(serializedNotification, recipientUserId, ','); - getline(serializedNotification, title, ','); - getline(serializedNotification, message, ','); - getline(serializedNotification, createdAtTimestampString, ','); - util::Timestamp createdAtTimestamp; - try - { - createdAtTimestamp = util::Timestamp::fromString(createdAtTimestampString); - } - catch (...) - { - throw std::runtime_error("Invalid createdAt timestamp"); - } return Factory::getObject( - id, - recipientUserId, - title, - message, - createdAtTimestamp - ); -} - -/* -Function: getHeaders -Description: Retrieves the CSV headers for notification serialization. -Parameters: - - None -Returns: - - std::string: Header string ("ID,RecipientID,Title,Message,Timestamp") -*/ -std::string Notification::getHeaders() -{ - return "ID,RecipientID,Title,Message,Timestamp"; + serializedNotification.id, + serializedNotification.recipientUserId, + serializedNotification.title, + serializedNotification.message, + serializedNotification.createdAt, + serializedNotification.state); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h index 57869fa..d92bc2c 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h @@ -9,8 +9,10 @@ Date: 19-May-2026 #pragma once #include #include "Timestamp.h" +#include "Enums.h" class User; +struct SerializedNotification; class Notification { @@ -22,10 +24,11 @@ private: std::string m_title; std::string m_message; util::Timestamp m_createdAt; + util::State m_state; public: Notification(); Notification(const std::string& recipientUserId, User* recipient, const std::string& title, const std::string& message, const util::Timestamp& createdAt); - Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt); + Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt, const util::State& state); const std::string& getId() const; const std::string& getRecipientUserId() const; User* getRecipient() const; @@ -38,7 +41,8 @@ public: void setTitle(const std::string& title); void setMessage(const std::string& message); void setCreatedAt(const util::Timestamp& createdAt); - std::string serialize() const; - static Notification* deserialize(const std::string&); - static std::string getHeaders(); + util::State getState() const; + void setState(util::State state); + SerializedNotification serialize() const; + static Notification* deserialize(const SerializedNotification&); }; From 67ac7f66258cd8cefeea0b3b81ab4f5d09974448 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Fri, 12 Jun 2026 19:23:56 +0530 Subject: [PATCH 17/39] Implement Service Refactoring 1954: Implement Service Refactoring UserStory #1954 1. Refactored notification handling to persist notifications directly in the datastore instead of maintaining notification collections within User objects. 2. Removed recipient User pointer dependencies from Notification and retained recipient user identification through recipientUserId. 3. Implemented generic observer persistence support in DataStore with shared helper methods for loading and saving observer subscriptions. 4. Added datastore-backed observer management for ServiceManagementService, PaymentManagementService, and InventoryManagementService. 5. Updated attach() and detach() operations to load, modify, and persist observer subscriptions using shared memory mappings. 6. Refactored sendNotification() implementations to create and persist Notification records directly to the datastore for subscribed observers. 7. Updated UserManagementService notification retrieval and deletion logic to operate on datastore notification records filtered by recipient user ID. 8. Removed notification ownership and observer-specific notification APIs from User and Observer classes. 9. Added configurable shared memory growth factor support and updated mapping expansion logic to use centralized configuration values. 10. Removed obsolete NotificationManagementService implementation and updated project configuration references. 11. Added DataStoreLockGuard integration for observer and notification persistence operations to ensure synchronized datastore access. N/A Sreeja Reghukumar, please review --- .../Trenser.VehicleServiceSystem.vcxproj | 1 - ...enser.VehicleServiceSystem.vcxproj.filters | 6 +- .../core/patterns/Observer.h | 1 - .../datastores/DataStore.cpp | 89 +++++++++++++++---- .../datastores/DataStore.h | 2 + .../datastores/sharedmemory/SharedMemory.cpp | 2 +- .../models/Notification.cpp | 35 +------- .../models/Notification.h | 5 +- .../models/User.cpp | 44 --------- .../models/User.h | 5 +- .../services/InventoryManagementService.cpp | 50 ++++++----- .../NotificationManagementService.cpp | 1 - .../services/PaymentManagementService.cpp | 49 +++++----- .../services/ServiceManagementService.cpp | 77 +++++++--------- .../services/ServiceManagementService.h | 2 - .../services/UserManagementService.cpp | 26 +++--- .../utilities/Config.h | 1 + 17 files changed, 186 insertions(+), 210 deletions(-) delete mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.cpp diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 7c4bfd3..b9a6f28 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -140,7 +140,6 @@ - diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index 7e74b19..e9b5831 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -114,9 +114,6 @@ Source Files\DataStores - - Source Files\Services - Source Files\Core\Patterns @@ -278,5 +275,8 @@ Header Files\DataStores\SharedMemory + + Header Files + \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h index 7fd7262..e9eb955 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h @@ -13,5 +13,4 @@ class Observer { public: virtual ~Observer() = default; - virtual void addNotification(Notification* notification) = 0; }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index bda9ac0..9f72555 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -230,20 +230,6 @@ util::Map>& DataStore::getUsers() { auto users = loadRecords(m_users); refreshCache(m_userCache, users); - auto& notifications = getNotifications(); - int numberOfNotifications = m_notificationCache.getSize(); - for (int index = 0; index < numberOfNotifications; index++) - { - Notification* notification = notifications.getValueAt(index).data; - const std::string& recipientUserId = notification->getRecipientUserId(); - int userIndex = m_userCache.find(recipientUserId); - if (userIndex == -1) - { - throw std::runtime_error("Invalid recipient user ID"); - } - User* user = m_userCache.getValueAt(userIndex).data; - user->addNotification(notification); - } return m_userCache; } @@ -340,6 +326,37 @@ util::Map>& DataStore::getInvoices() return m_invoiceCache; } +/* +Function: getObservers +Description: Retrieves observer records from the specified observer mapping + and resolves them to User objects. +Parameters: + - mapping: Observer mapping to read from +Returns: + - util::Map: Collection of observer records +Throws: + - std::runtime_error if an observer references an invalid user ID +*/ +util::Map DataStore::getObservers(MappingInfo& mapping) +{ + auto& users = getUsers(); + util::Map observers; + SharedMemory::ensureLatestMapping(mapping); + size_t recordCount = SharedMemory::getRecordCount(mapping); + for (size_t index = 0; index < recordCount; index++) + { + const SerializedObserver* observer = static_cast(SharedMemory::getRecordAddress(mapping, index)); + int userIndex = users.find(observer->id); + if (userIndex == -1) + { + throw std::runtime_error("Invalid observer user ID"); + } + User* user = users.getValueAt(userIndex).data; + observers.insert(user->getId(), user); + } + return observers; +} + /* Function: getServiceManagementObservers Description: Retrieves all service management observer records from the datastore. @@ -350,7 +367,7 @@ Returns: */ util::Map DataStore::getServiceManagementObservers() { - return util::Map(); + return getObservers(m_serviceManagementObservers); } /* @@ -363,7 +380,7 @@ Returns: */ util::Map DataStore::getPaymentManagementObservers() { - return util::Map(); + return getObservers(m_paymentManagementObservers); } /* @@ -376,7 +393,7 @@ Returns: */ util::Map DataStore::getInventoryManagementObservers() { - return util::Map(); + return getObservers(m_inventoryManagementObservers); } /* @@ -390,7 +407,6 @@ Returns: void DataStore::saveUsers() { saveRecords(m_users, m_userCache); - saveNotifications(); } /* @@ -478,16 +494,49 @@ void DataStore::saveInvoices() { } +/* +Function: saveObservers +Description: Persists observer records to the specified observer mapping. +Parameters: + - mapping: MappingInfo&, observer mapping to save to + - observers: util::Map&, collection of observer records +Returns: + - None +*/ +void DataStore::saveObservers(MappingInfo& mapping, util::Map& observers) +{ + size_t observerCount = static_cast(observers.getSize()); + size_t capacity = config::file::INITIAL_CAPACITY; + while (capacity < observerCount) + { + capacity *= config::file::GROWTH_FACTOR; + } + if (!SharedMemory::resizeMapping(mapping, capacity)) + { + throw std::runtime_error("Failed to resize observer mapping"); + } + SharedMemory::setRecordCount(mapping, observerCount); + for (size_t index = 0; index < observerCount; index++) + { + SerializedObserver serializedObserver; + User* user = observers.getValueAt(static_cast(index)); + strcpy_s(serializedObserver.id, sizeof(serializedObserver.id), user->getId().c_str()); + SerializedObserver* destination = static_cast(SharedMemory::getRecordAddress(mapping, index)); + *destination = serializedObserver; + } +} + /* Function: saveServiceManagementObservers Description: Persists all service management observer records to the datastore. Parameters: - - observers: util::Map>&, collection of observer records + - observers: util::Map&, collection of observer records Returns: - None */ void DataStore::saveServiceManagementObservers(util::Map& observers) { + saveObservers(m_serviceManagementObservers, observers); } /* @@ -500,6 +549,7 @@ Returns: */ void DataStore::savePaymentManagementObservers(util::Map& observers) { + saveObservers(m_paymentManagementObservers, observers); } /* @@ -512,6 +562,7 @@ Returns: */ void DataStore::saveInventoryManagementObservers(util::Map& observers) { + saveObservers(m_inventoryManagementObservers, observers); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 3950b11..62c585d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -86,6 +86,8 @@ private: void saveRecords(MappingInfo& mapping, util::Map>& records); template void clearCache(util::Map>&cache); template void refreshCache(util::Map>&cache, util::Map>&refreshedCache); + util::Map getObservers(MappingInfo& mapping); + void saveObservers(MappingInfo& mapping, util::Map& observers); }; /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp index 4b151e3..0c5c7b1 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp @@ -320,7 +320,7 @@ bool SharedMemory::ensureCapacityForInsert(MappingInfo& mapping) { return true; } - return resizeMapping(mapping, capacity * 2); + return resizeMapping(mapping, capacity * config::file::GROWTH_FACTOR); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp index ad1b416..83532ec 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp @@ -1,7 +1,7 @@ /* File: Notification.cpp Description: Implements the Notification class which represents system notifications in the Vehicle Service Management System. - Provides constructors, accessors, and mutators for notification details such as ID, recipient, title, message, and timestamp. + Provides constructors, accessors, and mutators for notification details such as ID, recipientID, title, message, and timestamp. Author: Trenser Date: 19-May-2026 */ @@ -23,9 +23,8 @@ Returns: - A new Notification object. */ Notification::Notification() - : m_id("NOT" + std::to_string(++m_uid)), - m_recipient(nullptr), m_state(util::State::ACTIVE) { -} + : m_id("NOT" + std::to_string(++m_uid)), + m_state(util::State::ACTIVE) {} /* Function: Notification @@ -39,10 +38,9 @@ Parameters: Returns: - A new Notification object. */ -Notification::Notification(const std::string& recipientUserId, User* recipient, const std::string& title, const std::string& message, const util::Timestamp& createdAt) +Notification::Notification(const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt) : m_id("NOT" + std::to_string(++m_uid)), m_recipientUserId(recipientUserId), - m_recipient(recipient), m_title(title), m_message(message), m_state(util::State::ACTIVE), @@ -65,7 +63,6 @@ Returns: Notification::Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt, const util::State& state) : m_id(id), m_recipientUserId(recipientUserId), - m_recipient(nullptr), m_title(title), m_message(message), m_createdAt(createdAt), @@ -100,17 +97,6 @@ const std::string& Notification::getRecipientUserId() const return m_recipientUserId; } -/* -Function: getRecipient -Description: Retrieves the pointer to the recipient user. -Returns: - - User* representing the recipient. -*/ -User* Notification::getRecipient() const -{ - return m_recipient; -} - /* Function: getTitle Description: Retrieves the title of the notification. @@ -181,19 +167,6 @@ void Notification::setRecipientUserId(const std::string& recipientUserId) m_recipientUserId = recipientUserId; } -/* -Function: setRecipient -Description: Sets the recipient user pointer for the notification. -Parameters: - - recipient: Pointer to the User object. -Returns: - - void -*/ -void Notification::setRecipient(User* recipient) -{ - m_recipient = recipient; -} - /* Function: setTitle Description: Sets the title of the notification. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h index d92bc2c..a102cc6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.h @@ -20,24 +20,21 @@ private: static int m_uid; std::string m_id; std::string m_recipientUserId; - User* m_recipient; std::string m_title; std::string m_message; util::Timestamp m_createdAt; util::State m_state; public: Notification(); - Notification(const std::string& recipientUserId, User* recipient, const std::string& title, const std::string& message, const util::Timestamp& createdAt); + Notification(const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt); Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt, const util::State& state); const std::string& getId() const; const std::string& getRecipientUserId() const; - User* getRecipient() const; const std::string& getTitle() const; const std::string& getMessage() const; const util::Timestamp& getCreatedAt() const; void setId(const std::string& id); void setRecipientUserId(const std::string& recipientUserId); - void setRecipient(User* recipient); void setTitle(const std::string& title); void setMessage(const std::string& message); void setCreatedAt(const util::Timestamp& createdAt); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp index c4dc928..5794259 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp @@ -89,23 +89,6 @@ User::User(const std::string& userId, const std::string& userName, const std::st } } -/* -Function: ~User -Description: Destructor that cleans up dynamically allocated notifications associated with the user. -Parameters: - - None -Returns: - - void -*/ -User::~User() -{ - auto values = m_notifications.getValues(); - for (int index = 0; index < values.getSize(); index++) - { - delete values[index]; - } -} - /* Function: getId Description: Retrieves the unique ID of the user. @@ -172,17 +155,6 @@ const std::string& User::getEmail() const return m_email; } -/* -Function: getNotifications -Description: Retrieves the map of notifications associated with the user. -Returns: - - util::Map& representing the notifications. -*/ -util::Map& User::getNotifications() -{ - return m_notifications; -} - /* Function: getUserType Description: Retrieves the role of the user. @@ -283,22 +255,6 @@ void User::setEmail(const std::string& email) m_email = email; } -/* -Function: addNotification -Description: Adds a new notification to the user’s notification map. -Parameters: - - notification: Pointer to the Notification object. -Returns: - - void -*/ -void User::addNotification(Notification* notification) -{ - if (notification) - { - m_notifications.insert(notification->getId(), notification); - } -} - /* Function: setRole Description: Sets the role of the user. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h index 1026bd3..1a0f93f 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.h @@ -26,21 +26,19 @@ private: std::string m_name; std::string m_phone; std::string m_email; - util::Map m_notifications; util::UserType m_type; util::State m_status; public: User(); User(const std::string& userName, const std::string& password, const std::string& name, const std::string& phone, const std::string& email, util::UserType role); User(const std::string& userId, const std::string& userName, const std::string& password, const std::string& name, const std::string& phone, const std::string& email, util::UserType role, util::State status); - ~User(); + ~User() = default; const std::string& getId() const; const std::string& getUserName() const; const std::string& getPassword() const; const std::string& getName() const; const std::string& getPhone() const; const std::string& getEmail() const; - util::Map& getNotifications(); util::UserType getUserType() const; util::State getState() const; void setId(const std::string& id); @@ -49,7 +47,6 @@ public: void setName(const std::string& name); void setPhone(const std::string& phone); void setEmail(const std::string& email); - void addNotification(Notification* notification) override; void setRole(util::UserType role); void setState(util::State status); SerializedUser serialize() const; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp index d08d957..aed6f3a 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -19,7 +19,7 @@ Date: 22-May-2026 #include "User.h" #include "Utility.h" #include "Vector.h" - +#include "DataStoreLockGuard.h" util::Map InventoryManagementService::m_observers{}; @@ -271,6 +271,9 @@ Returns: */ void InventoryManagementService::attach(User* user) { + DataStoreLockGuard lock(m_dataStore); + m_observers.clear(); + m_observers = m_dataStore.getInventoryManagementObservers(); if (user) { const std::string& userID = user->getId(); @@ -279,6 +282,7 @@ void InventoryManagementService::attach(User* user) m_observers[userID] = user; } } + m_dataStore.saveInventoryManagementObservers(m_observers); } /* @@ -291,6 +295,9 @@ Returns: */ void InventoryManagementService::detach(User* user) { + DataStoreLockGuard lock(m_dataStore); + m_observers.clear(); + m_observers = m_dataStore.getInventoryManagementObservers(); if (user) { const std::string& userID = user->getId(); @@ -299,6 +306,7 @@ void InventoryManagementService::detach(User* user) m_observers.remove(userID); } } + m_dataStore.saveInventoryManagementObservers(m_observers); } /* @@ -315,27 +323,27 @@ Throws: */ void InventoryManagementService::sendNotification(User* user, const std::string& title, const std::string& message) { - if (user) + if (!user) { - if (m_observers.find(user->getId()) != -1) - { - Notification* notification = - Factory::getObject( - user->getId(), - user, - title, - message, - util::Timestamp() - ); - if (notification) - { - user->addNotification(notification); - } - else - { - throw std::runtime_error("Failed to create notification"); - } - } + return; } + DataStoreLockGuard lock(m_dataStore); + m_observers = m_dataStore.getInventoryManagementObservers(); + if (m_observers.find(user->getId()) == -1) + { + return; + } + Notification* notification = Factory::getObject( + user->getId(), + title, + message, + util::Timestamp()); + if (!notification) + { + throw std::runtime_error("Failed to create notification"); + } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); + m_dataStore.saveNotifications(); } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.cpp deleted file mode 100644 index cc23059..0000000 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "NotificationManagementService.h" diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index 1fc1ff8..1ebb273 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -21,6 +21,7 @@ Date: 20-May-2026 #include "Timestamp.h" #include "User.h" #include "Utility.h" +#include "DataStoreLockGuard.h" util::Map PaymentManagementService::m_observers{}; @@ -34,6 +35,9 @@ Returns: */ void PaymentManagementService::attach(User* user) { + DataStoreLockGuard lock(m_dataStore); + m_observers.clear(); + m_observers = m_dataStore.getPaymentManagementObservers(); if (user) { const std::string& userID = user->getId(); @@ -42,6 +46,7 @@ void PaymentManagementService::attach(User* user) m_observers[userID] = user; } } + m_dataStore.savePaymentManagementObservers(m_observers); } /* @@ -54,6 +59,9 @@ Returns: */ void PaymentManagementService::detach(User* user) { + DataStoreLockGuard lock(m_dataStore); + m_observers.clear(); + m_observers = m_dataStore.getPaymentManagementObservers(); if (user) { const std::string& userID = user->getId(); @@ -62,6 +70,7 @@ void PaymentManagementService::detach(User* user) m_observers.remove(userID); } } + m_dataStore.savePaymentManagementObservers(m_observers); } /* @@ -78,28 +87,28 @@ Throws: */ void PaymentManagementService::sendNotification(User* user, const std::string& title, const std::string& message) { - if (user) + if (!user) { - if (m_observers.find(user->getId()) != -1) - { - Notification* notification = - Factory::getObject( - user->getId(), - user, - title, - message, - util::Timestamp() - ); - if (notification) - { - user->addNotification(notification); - } - else - { - throw std::runtime_error("Failed to create notification"); - } - } + return; } + DataStoreLockGuard lock(m_dataStore); + m_observers = m_dataStore.getPaymentManagementObservers(); + if (m_observers.find(user->getId()) == -1) + { + return; + } + Notification* notification = Factory::getObject( + user->getId(), + title, + message, + util::Timestamp()); + if (!notification) + { + throw std::runtime_error("Failed to create notification"); + } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); + m_dataStore.saveNotifications(); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 1a9f753..102ba31 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -26,6 +26,7 @@ Date:19-May-2026 #include "User.h" #include "UserManagementService.h" #include "Utility.h" +#include "DataStoreLockGuard.h" /* Function: purchaseService @@ -122,6 +123,9 @@ Returns: */ void ServiceManagementService::attach(User* user) { + DataStoreLockGuard lock(m_dataStore); + m_observers.clear(); + m_observers = m_dataStore.getServiceManagementObservers(); if (user) { const std::string& userID = user->getId(); @@ -130,6 +134,7 @@ void ServiceManagementService::attach(User* user) m_observers[userID] = user; } } + m_dataStore.saveServiceManagementObservers(m_observers); } /* @@ -142,6 +147,9 @@ Returns: */ void ServiceManagementService::detach(User* user) { + DataStoreLockGuard lock(m_dataStore); + m_observers.clear(); + m_observers = m_dataStore.getServiceManagementObservers(); if (user) { const std::string& userID = user->getId(); @@ -150,6 +158,7 @@ void ServiceManagementService::detach(User* user) m_observers.remove(userID); } } + m_dataStore.saveServiceManagementObservers(m_observers); } /* @@ -166,52 +175,28 @@ Throws: */ void ServiceManagementService::sendNotification(User* user, const std::string& title, const std::string& message) { - if (user) - { - if (m_observers.find(user->getId()) != -1) - { - Notification* notification = - Factory::getObject( - user->getId(), - user, - title, - message, - util::Timestamp() - ); - if (notification) - { - user->addNotification(notification); - } - else - { - throw std::runtime_error("Failed to create notification"); - } - } - } -} - -/* -Function: getObserverIDs -Description: Retrieves the IDs of all observers currently attached to the - ServiceManagementService. -Parameters: - - None -Returns: - - util::Vector: Vector of observer user IDs -*/ -util::Vector ServiceManagementService::getObserverIDs() -{ - util::Vector observerIDs; - int numberOfObservers = m_observers.getSize(); - for (int index = 0; index < numberOfObservers; index++) - { - User* observer = m_observers.getValueAt(index); - if (observer) - { - observerIDs.push_back(observer->getId()); - } - } - return observerIDs; + if (!user) + { + return; + } + DataStoreLockGuard lock(m_dataStore); + m_observers = m_dataStore.getServiceManagementObservers(); + if (m_observers.find(user->getId()) == -1) + { + return; + } + Notification* notification = Factory::getObject( + user->getId(), + title, + message, + util::Timestamp()); + if (!notification) + { + throw std::runtime_error("Failed to create notification"); + } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); + m_dataStore.saveNotifications(); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h index b2a4384..f1ff966 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h @@ -53,6 +53,4 @@ public: void saveServiceBookings(); void loadJobCards(); void saveJobCards(); - void loadObservers(); - void saveObservers(); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 9b1c61c..1be3a40 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -170,12 +170,16 @@ util::Vector UserManagementService::getUserNotifications(const st User* user = trackedUsersMap[userID].data; if (user) { - auto& notifications = user->getNotifications(); - int numberOfNotifications = notifications.getSize(); + auto& trackedNotificationMap = m_dataStore.getNotifications(); + int numberOfNotifications = trackedNotificationMap.getSize(); util::Vector notificationsVector; for (int index = 0; index < numberOfNotifications; index++) { - notificationsVector.push_back(notifications.getValueAt(index)); + Notification* notification = trackedNotificationMap.getValueAt(index).data; + if (notification->getRecipientUserId() == userID && notification->getState() == util::State::ACTIVE) + { + notificationsVector.push_back(notification); + } } return notificationsVector; } @@ -208,20 +212,18 @@ void UserManagementService::deleteNotification(const std::string& notificationID { throw std::runtime_error("No user found with given UserID"); } - User* user = trackedUsersMap.getValueAt(userIndex).data; - auto& notifications = user->getNotifications(); - if (notifications.find(notificationID) == -1) - { - throw std::runtime_error("No notification found with given NotificationID"); - } int notificationIndex = trackedNotificationsMap.find(notificationID); if (notificationIndex == -1) { throw std::runtime_error("No notification found with given NotificationID"); } - notifications[notificationID]->setState(util::State::INACTIVE); - trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED; - m_dataStore.saveNotifications(); + Notification* notification = trackedNotificationsMap.getValueAt(notificationIndex).data; + if (notification->getRecipientUserId() == userID) + { + notification->setState(util::State::INACTIVE); + trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED; + m_dataStore.saveNotifications(); + } } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h index 2eef19d..a2995b4 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h @@ -29,6 +29,7 @@ namespace config namespace file { const size_t INITIAL_CAPACITY = 100; + const size_t GROWTH_FACTOR = 2; constexpr const char* DIRECTORY = "files/"; constexpr const char* INVENTORYITEM_FILE = "files/InventoryItem.dat"; constexpr const char* USER_FILE = "files/User.dat"; From 404d217504f8aeb1be3ba6a191f1cec52e58dc9c Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Sun, 14 Jun 2026 14:11:03 +0530 Subject: [PATCH 18/39] Clean up legacy code - Remove FileManager and related file-based persistence logic - Remove obsolete observer persistence utilities - Remove unused service APIs and includes - Delete duplicate DataStore mutex stubs - Perform general dead-code cleanup --- .gitignore | 4 +- .../Trenser.VehicleServiceSystem.vcxproj | 1 - ...enser.VehicleServiceSystem.vcxproj.filters | 5 +- .../core/patterns/Observer.h | 2 - .../datastores/DataStore.cpp | 32 -- .../datastores/DataStore.h | 17 +- .../models/ComboPackage.h | 1 + .../services/InventoryManagementService.cpp | 90 ------ .../services/InventoryManagementService.h | 5 - .../services/NotificationManagementService.h | 1 - .../services/PaymentManagementService.cpp | 118 ------- .../services/PaymentManagementService.h | 5 - .../services/ServiceManagementService.cpp | 294 ------------------ .../services/ServiceManagementService.h | 9 - .../services/UserManagementService.cpp | 1 - .../utilities/FileHelper.h | 63 ---- .../utilities/FileManager.h | 119 ------- .../utilities/InputHelper.h | 2 +- .../utilities/Utility.h | 46 --- 19 files changed, 13 insertions(+), 802 deletions(-) delete mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileManager.h diff --git a/.gitignore b/.gitignore index fe37065..37b6ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -427,5 +427,5 @@ FodyWeavers.xsd *.msm *.msp -# CSV Files -*.csv +# DAT Files +*.dat diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index b9a6f28..34bfc34 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -181,7 +181,6 @@ - diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index e9b5831..c13b320 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -242,9 +242,6 @@ Header Files\Utilities - - Header Files\Utilities - Header Files\Utilities @@ -276,7 +273,7 @@ Header Files\DataStores\SharedMemory - Header Files + Header Files\DataStores \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h index e9eb955..98e6f97 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h @@ -7,8 +7,6 @@ Date: 19-May-2026 #pragma once -class Notification; - class Observer { public: diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 9f72555..52b3e57 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -144,11 +144,6 @@ bool DataStore::initialize() success = false; break; } - if (!SharedMemory::createOrOpenMapping(m_payments)) - { - success = false; - break; - } if (!SharedMemory::createOrOpenMapping(m_serviceManagementObservers)) { success = false; @@ -193,7 +188,6 @@ void DataStore::shutdown() SharedMemory::closeMapping(m_serviceBookings); SharedMemory::closeMapping(m_jobCards); SharedMemory::closeMapping(m_invoices); - SharedMemory::closeMapping(m_payments); SharedMemory::closeMapping(m_serviceManagementObservers); SharedMemory::closeMapping(m_paymentManagementObservers); SharedMemory::closeMapping(m_inventoryManagementObservers); @@ -565,32 +559,6 @@ void DataStore::saveInventoryManagementObservers(util::Map& saveObservers(m_inventoryManagementObservers, observers); } -/* -Function: lockDataStore -Description: Acquires exclusive access to the datastore. -Parameters: - - None -Returns: - - bool: True if the datastore was successfully locked, otherwise false -*/ -bool DataStore::lockDataStore() -{ - return false; -} - -/* -Function: unlockDataStore -Description: Releases exclusive access to the datastore. -Parameters: - - None -Returns: - - bool: True if the datastore was successfully unlocked, otherwise false -*/ -bool DataStore::unlockDataStore() -{ - return false; -} - /* Function: lockDataStore Description: Acquires the datastore mutex, providing diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 62c585d..34268f4 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -12,14 +12,14 @@ Date: 19-May-2026 #include "MappingInfo.h" #include "TrackedRecord.h" #include "SharedMemory.h" -class User; -class Notification; -class Service; -class ComboPackage; -class InventoryItem; -class ServiceBooking; -class JobCard; -class Invoice; +#include "User.h" +#include "Notification.h" +#include "Service.h" +#include "ComboPackage.h" +#include "InventoryItem.h" +#include "ServiceBooking.h" +#include "JobCard.h" +#include "Invoice.h" class DataStore { @@ -39,7 +39,6 @@ private: MappingInfo m_serviceBookings; MappingInfo m_jobCards; MappingInfo m_invoices; - MappingInfo m_payments; MappingInfo m_serviceManagementObservers; MappingInfo m_paymentManagementObservers; MappingInfo m_inventoryManagementObservers; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h index 941c2b3..29be6a9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ComboPackage.h @@ -12,6 +12,7 @@ Date: 19-May-2026 #include "Enums.h" class Service; +struct SerializedComboPackage; class ComboPackage { diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp index aed6f3a..32e2453 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -12,7 +12,6 @@ Date: 22-May-2026 #include "Config.h" #include "Enums.h" #include "Factory.h" -#include "FileManager.h" #include "InventoryItem.h" #include "InventoryManagementService.h" #include "Timestamp.h" @@ -90,95 +89,6 @@ void InventoryManagementService::sendLowStockAlerts() } } -/* -Function: getObserverIDs -Description: Retrieves the IDs of all observers currently attached to the - InventoryManagementService. -Parameters: - - None -Returns: - - util::Vector: Vector of observer user IDs -*/ -util::Vector InventoryManagementService::getObserverIDs() -{ - util::Vector observerIDs; - int numberOfObservers = m_observers.getSize(); - for (int index = 0; index < numberOfObservers; index++) - { - User* observer = m_observers.getValueAt(index); - if (observer) - { - observerIDs.push_back(observer->getId()); - } - } - return observerIDs; -} - -/* -Function: loadInventoryItems -Description: Loads inventory items from persistent storage into the datastore. - Uses FileManager to deserialize inventory items from the configured file. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::loadInventoryItems() -{ - util::FileManager inventoryItemFileManager(config::file::INVENTORYITEM_FILE); - auto& inventoryItems = m_dataStore.getInventoryItems(); - auto inventoryItemsMap = inventoryItemFileManager.load(); - int numberOfInventoryItems = inventoryItemsMap.getSize(); - for (int index = 0; index < numberOfInventoryItems; index++) - { - inventoryItems[inventoryItemsMap.getKeyAt(index)] = inventoryItemsMap.getValueAt(index); - } -} - -/* -Function: saveInventoryItems -Description: Saves inventory items from the datastore to persistent storage. - Uses FileManager to serialize inventory items into the configured file. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::saveInventoryItems() -{ - util::FileManager inventoryItemFileManager(config::file::INVENTORYITEM_FILE); - auto& inventoryItems = m_dataStore.getInventoryItems(); - inventoryItemFileManager.save(inventoryItems); -} - -/* -Function: loadObservers -Description: Loads observer IDs from persistent storage and attaches corresponding - users as observers to the InventoryManagementService. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::loadObservers() -{ - util::loadObservers(config::file::INVENTORYMANAGEMENTOBSERVERS, this, m_dataStore); -} - -/* -Function: saveObservers -Description: Saves the current observer IDs of the InventoryManagementService - to persistent storage for future retrieval. -Parameters: - - None -Returns: - - void -*/ -void InventoryManagementService::saveObservers() -{ - util::saveObservers(config::file::INVENTORYMANAGEMENTOBSERVERS, this); -} - /* Function: addInventoryItem Description: Creates a new inventory item using the Factory and inserts it diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h index f5db383..2c3736c 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.h @@ -21,7 +21,6 @@ class InventoryManagementService : public NotificationManagementService private: DataStore& m_dataStore; static util::Map m_observers; - util::Vector getObserverIDs() override; public: InventoryManagementService() : m_dataStore(DataStore::getInstance()) {} util::Map getInventoryItems(); @@ -33,8 +32,4 @@ public: void sendNotification(User* user, const std::string& title, const std::string& message) override; void attach(User* user) override; void detach(User* user) override; - void loadInventoryItems(); - void saveInventoryItems(); - void loadObservers(); - void saveObservers(); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.h index 33d214a..18d0dda 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/NotificationManagementService.h @@ -19,5 +19,4 @@ public: virtual void sendNotification(User* recipient, const std::string& title, const std::string& message) = 0; virtual void attach(User* user) = 0; virtual void detach(User* user) = 0; - virtual util::Vector getObserverIDs() = 0; }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index 1ebb273..b8b9af1 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -11,7 +11,6 @@ Date: 20-May-2026 #include "Config.h" #include "Enums.h" #include "Factory.h" -#include "FileManager.h" #include "InventoryItem.h" #include "Invoice.h" #include "JobCard.h" @@ -149,123 +148,6 @@ void PaymentManagementService::sendPaymentReminders() } } -/* -Function: getObserverIDs -Description: Retrieves the IDs of all observers currently attached to the - PaymentManagementService. -Parameters: - - None -Returns: - - util::Vector: Vector of observer user IDs -*/ -util::Vector PaymentManagementService::getObserverIDs() -{ - util::Vector observerIDs; - int numberOfObservers = m_observers.getSize(); - for (int index = 0; index < numberOfObservers; index++) - { - User* observer = m_observers.getValueAt(index); - if (observer) - { - observerIDs.push_back(observer->getId()); - } - } - return observerIDs; -} - -/* -Function: loadInvoices -Description: Loads invoices from persistent storage into the datastore. - Validates associated service bookings and inventory parts before - attaching them to each invoice. Throws exceptions if invalid IDs - are encountered. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if a booking ID or part ID is invalid -*/ -void PaymentManagementService::loadInvoices() -{ - util::FileManager invoiceFileManager(config::file::INVOICE_FILE); - auto& invoices = m_dataStore.getInvoices(); - auto& serviceBookings = m_dataStore.getServiceBookings(); - auto& inventoryItems = m_dataStore.getInventoryItems(); - auto invoicesMap = invoiceFileManager.load(); - for (int invoiceIndex = 0; invoiceIndex < invoicesMap.getSize(); invoiceIndex++) - { - Invoice* invoice = invoicesMap.getValueAt(invoiceIndex); - int bookingIndex = serviceBookings.find(invoice->getBookingId()); - if (bookingIndex == -1) - { - throw std::runtime_error("Invalid Booking ID"); - } - ServiceBooking* booking = serviceBookings.getValueAt(bookingIndex); - invoice->setBooking(booking); - util::Map invoiceParts; - auto& partIDs = invoice->getPartIDs(); - for (int partIndex = 0; partIndex < partIDs.getSize(); partIndex++) - { - const std::string& partID = partIDs[partIndex]; - int inventoryIndex = inventoryItems.find(partID); - if (inventoryIndex == -1) - { - throw std::runtime_error("Invalid Part ID"); - } - invoiceParts[partID] = inventoryItems.getValueAt(inventoryIndex); - } - invoice->setParts(invoiceParts); - invoices[invoice->getId()] = invoice; - } -} - -/* -Function: saveInvoices -Description: Saves invoices from the datastore to persistent storage. - Uses FileManager to serialize invoices into the configured file. -Parameters: - - None -Returns: - - void -*/ -void PaymentManagementService::saveInvoices() -{ - util::FileManager invoiceFileManager(config::file::INVOICE_FILE); - auto& invoices = m_dataStore.getInvoices(); - invoiceFileManager.save(invoices); -} - -/* -Function: loadObservers -Description: Loads observer IDs from persistent storage and attaches corresponding - users as observers to the PaymentManagementService. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if an observer ID is invalid (not found in datastore) -*/ -void PaymentManagementService::loadObservers() -{ - util::loadObservers(config::file::PAYMENTMANAGEMENTOBSERVERS, this, m_dataStore); -} - -/* -Function: saveObservers -Description: Saves the current observer IDs of the PaymentManagementService - to persistent storage for future retrieval. -Parameters: - - None -Returns: - - void -*/ -void PaymentManagementService::saveObservers() -{ - util::saveObservers(config::file::PAYMENTMANAGEMENTOBSERVERS, this); -} - /* Function: createInventoryItemsMap (static helper) Description: Builds a map of inventory items required for a given service and adds them to the booking’s inventory map. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h index 8961a2d..f6e2e75 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h @@ -22,7 +22,6 @@ class PaymentManagementService : public NotificationManagementService private: DataStore& m_dataStore; static util::Map m_observers; - util::Vector getObserverIDs() override; public: PaymentManagementService() : m_dataStore(DataStore::getInstance()) {} void generateInvoice(ServiceBooking* booking); @@ -34,8 +33,4 @@ public: void sendNotification(User* user, const std::string& title, const std::string& message) override; void attach(User* user) override; void detach(User* user) override; - void loadInvoices(); - void saveInvoices(); - void loadObservers(); - void saveObservers(); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 102ba31..e294249 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -14,7 +14,6 @@ Date:19-May-2026 #include "DataStore.h" #include "Enums.h" #include "Factory.h" -#include "FileManager.h" #include "InventoryItem.h" #include "JobCard.h" #include "NotificationManagementService.h" @@ -199,299 +198,6 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t m_dataStore.saveNotifications(); } -/* -Function: loadServices -Description: Loads services from persistent storage into the datastore. - Validates required inventory items and attaches them to each service. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if an inventory item ID is invalid -*/ -void ServiceManagementService::loadServices() -{ - util::FileManager serviceFileManager(config::file::SERVICE_FILE); - auto& services = m_dataStore.getServices(); - auto& inventoryItems = m_dataStore.getInventoryItems(); - auto servicesMap = serviceFileManager.load(); - for (int serviceIndex = 0; serviceIndex < servicesMap.getSize(); serviceIndex++) - { - Service* service = servicesMap.getValueAt(serviceIndex); - services[service->getId()] = service; - util::Map inventoryItemsMap; - auto& inventoryItemIDs = service->getRequiredInventoryItemIDs(); - for (int inventoryItemIndex = 0; inventoryItemIndex < inventoryItemIDs.getSize(); inventoryItemIndex++) - { - const std::string& inventoryItemID = inventoryItemIDs[inventoryItemIndex]; - int index = inventoryItems.find(inventoryItemID); - if (index == -1) - { - throw std::runtime_error("Invalid Inventory Item ID"); - } - inventoryItemsMap[inventoryItemID] = inventoryItems.getValueAt(index); - } - service->setRequiredInventoryItems(inventoryItemsMap); - } -} - -/* -Function: saveServices -Description: Saves services from the datastore to persistent storage. - Uses FileManager to serialize services into the configured file. -Parameters: - - None -Returns: - - void -*/ -void ServiceManagementService::saveServices() -{ - util::FileManager serviceFileManager(config::file::SERVICE_FILE); - auto& services = m_dataStore.getServices(); - serviceFileManager.save(services); -} - -/* -Function: loadComboPackages -Description: Loads combo packages from persistent storage into the datastore. - Validates associated services and attaches them to each package. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if a service ID is invalid -*/ -void ServiceManagementService::loadComboPackages() -{ - util::FileManager comboPackageFileManager(config::file::COMBOPACKAGE_FILE); - auto& comboPackages = m_dataStore.getComboPackages(); - auto& services = m_dataStore.getServices(); - auto comboPackagesMap = comboPackageFileManager.load(); - for (int packageIndex = 0; packageIndex < comboPackagesMap.getSize(); packageIndex++) - { - ComboPackage* comboPackage = comboPackagesMap.getValueAt(packageIndex); - util::Map packageServices; - auto& serviceIDs = comboPackage->getServiceIDs(); - for (int serviceIndex = 0; serviceIndex < serviceIDs.getSize(); serviceIndex++) - { - const std::string& serviceID = serviceIDs[serviceIndex]; - int serviceMapIndex = services.find(serviceID); - if (serviceMapIndex == -1) - { - throw std::runtime_error("Invalid Service ID"); - } - packageServices[serviceID] = services.getValueAt(serviceMapIndex); - } - comboPackage->setServices(packageServices); - comboPackages[comboPackage->getId()] = comboPackage; - } -} - -/* -Function: saveComboPackages -Description: Saves combo packages from the datastore to persistent storage. - Uses FileManager to serialize combo packages into the configured file. -Parameters: - - None -Returns: - - void -*/ -void ServiceManagementService::saveComboPackages() -{ - util::FileManager comboPackageFileManager(config::file::COMBOPACKAGE_FILE); - auto& comboPackages = m_dataStore.getComboPackages(); - comboPackageFileManager.save(comboPackages); -} - -/* -Function: loadServiceBookings -Description: Loads service bookings from persistent storage into the datastore. - Validates associated services, customers, and technicians before - attaching them to each booking. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if a service ID, customer ID, or technician ID is invalid - - std::runtime_error if a user is not of the expected type (customer/technician) -*/ -void ServiceManagementService::loadServiceBookings() -{ - util::FileManager bookingFileManager(config::file::SERVICEBOOKING_FILE); - auto& serviceBookings = m_dataStore.getServiceBookings(); - auto& services = m_dataStore.getServices(); - auto& users = m_dataStore.getUsers(); - auto bookingsMap = bookingFileManager.load(); - for (int bookingIndex = 0; bookingIndex < bookingsMap.getSize(); bookingIndex++) - { - ServiceBooking* booking = bookingsMap.getValueAt(bookingIndex); - util::Map bookingServices; - auto& serviceIDs = booking->getServiceIDs(); - for (int serviceIndex = 0; serviceIndex < serviceIDs.getSize(); serviceIndex++) - { - const std::string& serviceID = serviceIDs[serviceIndex]; - int serviceMapIndex = services.find(serviceID); - if (serviceMapIndex == -1) - { - throw std::runtime_error("Invalid Service ID"); - } - - bookingServices[serviceID] = services.getValueAt(serviceMapIndex); - } - booking->setServices(bookingServices); - int customerIndex = users.find(booking->getCustomerId()); - if (customerIndex == -1) - { - throw std::runtime_error("Invalid Customer ID"); - } - User* customer = users.getValueAt(customerIndex); - if (customer->getUserType() != util::UserType::CUSTOMER) - { - throw std::runtime_error("User is not a customer"); - } - booking->setCustomer(customer); - const std::string& technicianId = booking->getAssignedTechnicianId(); - if (!technicianId.empty()) - { - int technicianIndex = users.find(technicianId); - if (technicianIndex == -1) - { - throw std::runtime_error("Invalid Technician ID"); - } - User* technician = users.getValueAt(technicianIndex); - if (technician->getUserType() != util::UserType::TECHNICIAN) - { - throw std::runtime_error("User is not a technician"); - } - booking->setAssignedTechnician(technician); - } - serviceBookings[booking->getId()] = booking; - } -} - -/* -Function: saveServiceBookings -Description: Saves service bookings from the datastore to persistent storage. - Uses FileManager to serialize bookings into the configured file. -Parameters: - - None -Returns: - - void -*/ -void ServiceManagementService::saveServiceBookings() -{ - util::FileManager bookingFileManager(config::file::SERVICEBOOKING_FILE); - auto& serviceBookings = m_dataStore.getServiceBookings(); - bookingFileManager.save(serviceBookings); -} - -/* -Function: loadJobCards -Description: Loads job cards from persistent storage into the datastore. - Validates associated bookings, services, and technicians before - attaching them to each job card. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if a booking ID, service ID, or technician ID is invalid - - std::runtime_error if a service does not belong to the booking - - std::runtime_error if a user is not a technician -*/ -void ServiceManagementService::loadJobCards() -{ - util::FileManager jobCardFileManager(config::file::JOBCARD_FILE); - auto& jobCards = m_dataStore.getJobCards(); - auto& serviceBookings = m_dataStore.getServiceBookings(); - auto& services = m_dataStore.getServices(); - auto& users = m_dataStore.getUsers(); - auto jobCardsMap = jobCardFileManager.load(); - for (int jobCardIndex = 0; jobCardIndex < jobCardsMap.getSize(); jobCardIndex++) - { - JobCard* jobCard = jobCardsMap.getValueAt(jobCardIndex); - int bookingIndex = serviceBookings.find(jobCard->getBookingId()); - if (bookingIndex == -1) - { - throw std::runtime_error("Invalid Booking ID"); - } - ServiceBooking* booking = serviceBookings.getValueAt(bookingIndex); - jobCard->setBooking(booking); - int serviceIndex = services.find(jobCard->getServiceId()); - if (serviceIndex == -1) - { - throw std::runtime_error("Invalid Service ID"); - } - Service* service = services.getValueAt(serviceIndex); - if (booking->getServices().find(jobCard->getServiceId()) == -1) - { - throw std::runtime_error("Service does not belong to booking"); - } - jobCard->setService(service); - int technicianIndex = users.find(jobCard->getTechnicianId()); - if (technicianIndex == -1) - { - throw std::runtime_error("Invalid Technician ID"); - } - User* technician = users.getValueAt(technicianIndex); - if (technician->getUserType() != util::UserType::TECHNICIAN) - { - throw std::runtime_error("User is not a technician"); - } - jobCard->setTechnician(technician); - jobCards[jobCard->getId()] = jobCard; - } -} - -/* -Function: saveJobCards -Description: Saves job cards from the datastore to persistent storage. - Uses FileManager to serialize job cards into the configured file. -Parameters: - - None -Returns: - - void -*/ -void ServiceManagementService::saveJobCards() -{ - util::FileManager jobCardFileManager(config::file::JOBCARD_FILE); - auto& jobCards = m_dataStore.getJobCards(); - jobCardFileManager.save(jobCards); -} - -/* -Function: loadObservers -Description: Loads observer IDs from persistent storage and attaches corresponding - users as observers to the ServiceManagementService. -Parameters: - - None -Returns: - - void -Throws: - - std::runtime_error if an observer ID is invalid (not found in datastore) -*/ -void ServiceManagementService::loadObservers() -{ - util::loadObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this, m_dataStore); -} - -/* -Function: saveObservers -Description: Saves the current observer IDs of the ServiceManagementService - to persistent storage for future retrieval. -Parameters: - - None -Returns: - - void -*/ -void ServiceManagementService::saveObservers() -{ - util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this); -} - /* Function: restoreInventory Description: Restores inventory quantities for all required items in the services associated diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h index f1ff966..2fcfd6d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h @@ -23,7 +23,6 @@ class ServiceManagementService : public NotificationManagementService private: DataStore& m_dataStore; static util::Map m_observers; - util::Vector getObserverIDs() override; public: ServiceManagementService() : m_dataStore(DataStore::getInstance()) {} util::Map getServices(); @@ -45,12 +44,4 @@ public: void sendNotification(User* user, const std::string& title, const std::string& message) override; void attach(User* user) override; void detach(User* user) override; - void loadServices(); - void saveServices(); - void loadComboPackages(); - void saveComboPackages(); - void loadServiceBookings(); - void saveServiceBookings(); - void loadJobCards(); - void saveJobCards(); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 1be3a40..e923e1c 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -11,7 +11,6 @@ Date:19-May-2026 #include "Config.h" #include "Enums.h" #include "Factory.h" -#include "FileManager.h" #include "InventoryManagementService.h" #include "Notification.h" #include "PaymentManagementService.h" diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h index c8bf96f..f73fba4 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileHelper.h @@ -43,67 +43,4 @@ namespace util position++; } } - - /* - Function: loadRecords - Description: Loads records from a given file path into a vector of strings. - Skips the header line if present. Creates the file if it does not exist. - Parameters: - - filePath: const std::string&, path to the file - Returns: - - util::Vector: Vector containing all records (excluding header) - Throws: - - None (creates file if missing) - */ - inline util::Vector loadRecords(const std::string& filePath) - { - util::Vector records; - std::ifstream file(filePath); - if (!file.is_open()) - { - ensureDirectoryExists(filePath); - std::ofstream newFile(filePath); - newFile.close(); - file.open(filePath); - } - std::string line; - bool isHeader = true; - while (std::getline(file, line)) - { - if (isHeader) - { - isHeader = false; - continue; - } - records.push_back(line); - } - return records; - } - - /* - Function: saveRecords - Description: Saves records to a given file path. Overwrites existing content - and writes a header line followed by all records. - Parameters: - - filePath: const std::string&, path to the file - - records: const util::Vector&, vector of records to save - Returns: - - void - Throws: - - std::runtime_error if the file cannot be opened for writing - */ - inline void saveRecords(const std::string& filePath, const util::Vector& records) - { - std::ofstream file(filePath, std::ios::trunc); - if (!file.is_open()) - { - throw std::runtime_error("Failed to open file " + filePath); - } - file << "Values" << '\n'; - int numberOfRecords = records.getSize(); - for (int index = 0; index < numberOfRecords; index++) - { - file << records[index] << '\n'; - } - } } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileManager.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileManager.h deleted file mode 100644 index 1ea66c7..0000000 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/FileManager.h +++ /dev/null @@ -1,119 +0,0 @@ -/* -File: FileManager.h -Description: Declares and implements a generic FileManager template class for - loading and saving objects to and from files. Uses serialization - and deserialization methods defined in the object type T. - Provides persistence support for system entities such as Users, - Services, InventoryItems, etc. -Author: Trenser -Date: 22-May-2026 -*/ - -#pragma once -#include -#include -#include -#include "Vector.h" -#include "Map.h" -#include "FileHelper.h" - -namespace util -{ - template using objects = util::Map; - - template - class FileManager - { - private: - std::string m_filePath; - public: - FileManager() : m_filePath("") {} - FileManager(const std::string& filePath) : m_filePath(filePath) {} - objects load(); - void save(const objects&); - }; - - /* - Function: load - Description: Loads records from the file into a map of objects. - Skips the header line, deserializes each record into an object of type T, - and stores them in a map keyed by object ID. - Parameters: - - None - Returns: - - util::Map containing deserialized objects - Throws: - - std::runtime_error if deserialization fails for any record - */ - template - objects FileManager::load() - { - objects records; - std::ifstream file(m_filePath); - if (!file.is_open()) - { - ensureDirectoryExists(m_filePath); - std::ofstream newFile(m_filePath); - newFile.close(); - file.open(m_filePath); - } - util::Vector lines; - std::string line; - while (std::getline(file, line)) - { - lines.push_back(line); - } - int numberOfLines = lines.getSize(); - bool isHeader = true; - for (int lineIndex = 0; lineIndex < numberOfLines; lineIndex++) - { - const auto& record = lines[lineIndex]; - if (isHeader) - { - isHeader = false; - continue; - } - auto object = T::deserialize(record); - if (!object) - { - throw std::runtime_error("Failed to deserialize record"); - } - records[object->getId()] = object; - } - return records; - } - - /* - Function: save - Description: Saves records to the file. Serializes each object of type T into a string, - writes a header line, and then writes all serialized records to the file. - Parameters: - - records: const util::Map&, map of objects to save - Returns: - - void - Throws: - - std::runtime_error if the file cannot be opened for writing - */ - template - void FileManager::save(const objects& records) - { - util::Vector lines; - lines.push_back(T::getHeaders()); - int numberOfRecords = records.getSize(); - for (int recordIndex = 0; recordIndex < numberOfRecords; recordIndex++) - { - const auto& record = records.getValueAt(recordIndex); - lines.push_back(record->serialize()); - } - std::ofstream file(m_filePath, std::ios::trunc); - if (!file.is_open()) - { - throw std::runtime_error("Failed to open file " + m_filePath); - } - int numberOfLines = lines.getSize(); - for (int lineIndex = 0; lineIndex < numberOfLines; lineIndex++) - { - file << lines[lineIndex] << '\n'; - } - } -} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h index dffc199..065a197 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/InputHelper.h @@ -28,7 +28,7 @@ namespace util if (!(std::cin >> value)) { std::cin.clear(); - std::cin.ignore(std::numeric_limits::max(), '\n'); + std::cin.ignore((std::numeric_limits::max)(), '\n'); throw std::runtime_error("Invalid console input"); } } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h index 9b6bc9d..56dd765 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h @@ -54,52 +54,6 @@ namespace util return cost; } - /* - Function: loadObservers - Description: Loads observer IDs from a file and attaches the corresponding users - to the notification management service. Validates that each observer ID - exists in the datastore before attaching. - Parameters: - - filePath: const std::string&, path to the file containing observer IDs - - service: NotificationManagementService*, pointer to the notification service - - dataStore: DataStore&, reference to the datastore containing users - Returns: - - void - Throws: - - std::runtime_error if an observer ID is invalid (not found in datastore) - */ - inline void loadObservers(const std::string& filePath, NotificationManagementService* service, DataStore& dataStore) - { - auto observerIDs = util::loadRecords(filePath); - auto& users = dataStore.getUsers(); - for (int index = 0; index < observerIDs.getSize(); index++) - { - const std::string& observerID = observerIDs[index]; - int userIndex = users.find(observerID); - if (userIndex == -1) - { - throw std::runtime_error("Invalid Observer ID"); - } - service->attach(users.getValueAt(userIndex)); - } - } - - /* - Function: saveObservers - Description: Saves the current observer IDs from the notification management service - to a file for persistence. - Parameters: - - filePath: const std::string&, path to the file where observer IDs will be saved - - service: NotificationManagementService*, pointer to the notification service - Returns: - - void - */ - inline void saveObservers(const std::string& filePath, NotificationManagementService* service) - { - auto observerIDs = service->getObserverIDs(); - util::saveRecords(filePath, observerIDs); - } - template Map getObjects(const Map>& trackedRecords); From f484c62a1e7f06f888d9316a615ccb7b5b2b495c Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Sun, 14 Jun 2026 17:00:49 +0530 Subject: [PATCH 19/39] Fix cache refresh handling for unsaved records - Preserve NEW_RECORD entries during cache refresh - Prevent accidental deletion of pending datastore changes - Clean up only stale persisted records --- .../datastores/DataStore.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 34268f4..265f8df 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -230,9 +230,17 @@ void DataStore::refreshCache(util::Map>& cac for (int index = 0; index < oldCache.getSize(); ++index) { const std::string& id = oldCache.getKeyAt(index); + const TrackedRecord& localTrackedRecord = oldCache.getValueAt(index); if (cache.find(id) == -1) { - delete oldCache.getValueAt(index).data; + if (localTrackedRecord.state == RecordState::NEW_RECORD) + { + cache.insert(id, localTrackedRecord); + } + else + { + delete localTrackedRecord.data; + } } } } \ No newline at end of file From 8aaa4eeec0bbe577099c8025f2728b95796aa07b Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Tue, 16 Jun 2026 11:02:39 +0530 Subject: [PATCH 20/39] Implement interprocess event handling for notifications and account disable Changes: - Implements #2061 - Introduce EventManager for user-specific Windows event publishing/listening - Add real-time notification and account-disabled event propagation - Register authentication events through Controller and AuthenticationManagementService - Trigger notification events from Inventory, Payment, and Service Management modules - Trigger account-disabled events when users are deactivated - Extract common menu event listener logic into Menu base class - Add notification popup handling for Admin, Customer, and Technician menus - Refactor shared memory components into core/sharedmemory - Update project structure and include paths for events and shared memory modules --- .../Trenser.VehicleServiceSystem.vcxproj | 22 +- ...enser.VehicleServiceSystem.vcxproj.filters | 66 +++-- .../controllers/Controller.cpp | 13 + .../controllers/Controller.h | 2 + .../core/events/EventManager.cpp | 225 ++++++++++++++++++ .../core/events/EventManager.h | 37 +++ .../sharedmemory/FileHeader.h | 0 .../sharedmemory/MappingInfo.h | 0 .../sharedmemory/RecordState.h | 0 .../sharedmemory/SerializedRecords.h | 0 .../sharedmemory/SharedMemory.cpp | 0 .../sharedmemory/SharedMemory.h | 0 .../sharedmemory/TrackedRecord.h | 0 .../AuthenticationManagementService.cpp | 36 +++ .../AuthenticationManagementService.h | 6 + .../services/InventoryManagementService.cpp | 2 + .../services/PaymentManagementService.cpp | 2 + .../services/ServiceManagementService.cpp | 2 + .../services/UserManagementService.cpp | 45 ++-- .../views/AdminMenu.cpp | 25 ++ .../views/AdminMenu.h | 5 +- .../views/CustomerMenu.cpp | 26 ++ .../views/CustomerMenu.h | 5 +- .../views/Menu.cpp | 144 +++++++++++ .../Trenser.VehicleServiceSystem/views/Menu.h | 33 +++ .../views/MenuHelper.h | 34 +++ .../views/TechnicianMenu.cpp | 25 ++ .../views/TechnicianMenu.h | 5 +- 28 files changed, 703 insertions(+), 57 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.cpp create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.h rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/FileHeader.h (100%) rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/MappingInfo.h (100%) rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/RecordState.h (100%) rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/SerializedRecords.h (100%) rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/SharedMemory.cpp (100%) rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/SharedMemory.h (100%) rename Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/{datastores => core}/sharedmemory/TrackedRecord.h (100%) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 34bfc34..e554d2b 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -102,7 +102,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;$(ProjectDir)datastores\sharedmemory;%(AdditionalIncludeDirectories) + $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;$(ProjectDir)core\sharedmemory;$(ProjectDir)core\events;%(AdditionalIncludeDirectories) Console @@ -117,7 +117,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;$(ProjectDir)datastores\sharedmemory;%(AdditionalIncludeDirectories) + $(ProjectDir)models;$(ProjectDir)controllers;$(ProjectDir)factories;$(ProjectDir)views;$(ProjectDir)services;$(ProjectDir)utilities;$(ProjectDir)core\patterns;$(ProjectDir)datastores;$(ProjectDir)core\sharedmemory;$(ProjectDir)core\events;%(AdditionalIncludeDirectories) Console @@ -126,10 +126,11 @@ + + - @@ -148,21 +149,23 @@ + + + + + + + + - - - - - - @@ -191,6 +194,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index c13b320..ac65e97 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -64,11 +64,17 @@ {8057b93d-51a9-42df-b06e-01ce395f6308} - - {ec639004-44c6-4bd6-9963-077adde82b5f} + + {d9da9793-fe6f-4914-bee3-99d5934da228} - - {7aa8722e-adfa-466e-8211-de63f3b7892b} + + {0769afb6-f57d-4ae3-a1cf-ceca6e606af0} + + + {85029bdb-6941-41dc-a3a7-9e5841671d8c} + + + {1050aca7-6f2c-4ccb-a446-db9c898c3599} @@ -144,8 +150,14 @@ Source Files\Models - - Source Files\DataStores\SharedMemory + + Source Files\Core\SharedMemory + + + Source Files\Core\Events + + + Source Files\Views @@ -254,26 +266,32 @@ Header Files\Views - - Header Files\DataStores\SharedMemory - - - Header Files\DataStores\SharedMemory - - - Header Files\DataStores\SharedMemory - - - Header Files\DataStores\SharedMemory - - - Header Files\DataStores\SharedMemory - - - Header Files\DataStores\SharedMemory - Header Files\DataStores + + Header Files\Core\SharedMemory + + + Header Files\Core\SharedMemory + + + Header Files\Core\SharedMemory + + + Header Files\Core\SharedMemory + + + Header Files\Core\SharedMemory + + + Header Files\Core\SharedMemory + + + Header Files\Core\Events + + + Header Files\Views + \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp index 487ecd5..d892e36 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp @@ -622,3 +622,16 @@ void Controller::shutdown() auto& dataStore = DataStore::getInstance(); dataStore.shutdown(); } + +/* +Function: registerEvents +Description: Registers menu event handles with the authentication + service. +Parameter: HANDLE accountDisabledEvent - account disabled event handle + HANDLE notificationAvailableEvent - notification event handle +Return type: void +*/ +void Controller::registerEvents(HANDLE accountDisabledEvent, HANDLE notificationAvailableEvent) +{ + m_authenticationManagementService.registerEvents(accountDisabledEvent, notificationAvailableEvent); +} diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h index 7ec1c30..622c7b7 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h @@ -8,6 +8,7 @@ Date:19-May-2026 */ #pragma once +#include #include #include "AuthenticationManagementService.h" #include "Enums.h" @@ -72,4 +73,5 @@ public: void configureNotifications(bool paymentNotifications, bool serviceNotifications); bool initialize(); void shutdown(); + void registerEvents(HANDLE accountDisabledEvent, HANDLE notificationAvailableEvent); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.cpp new file mode 100644 index 0000000..568a0e6 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.cpp @@ -0,0 +1,225 @@ +/* +File: EventManager.cpp +Description: Implementation file containing the method definitions of the + EventManager class, including listener management and + interprocess event publishing. +Author: Trenser +Date:15-Jun-2026 +*/ + +#include +#include +#include "EventManager.h" + +namespace +{ + const std::string USER_DISABLED_EVENT = "userDisabled_"; + + const std::string NOTIFICATION_AVAILABLE_EVENT = "notificationAvailable_"; +} + +/* +Function: EventManager +Description: Constructs an EventManager instance with default values. +Parameter: None +Return type: None +*/ +EventManager::EventManager() + : + m_userDisabledEvent(NULL), + m_notificationAvailableEvent(NULL), + m_shutdownEvent(NULL), + m_running(false) {} + +/* +Function: ~EventManager +Description: Destroys the EventManager and performs final cleanup. +Parameter: None +Return type: None +*/ +EventManager::~EventManager() +{ + shutdown(); + if (m_listenerThread.joinable()) + { + m_listenerThread.join(); + } +} + +/* +Function: initialize +Description: Creates the user-specific events and starts the listener + thread. +Parameter: const std::string& userId - unique identifier of the user + std::function userDisabledCallback - callback for + user disable events + std::function notificationCallback - callback for + notification events +Return type: bool - true if initialization succeeds, false otherwise +*/ +bool EventManager::initialize(const std::string& userId, std::function userDisabledCallback, std::function notificationCallback) +{ + if (m_running.load()) + { + return false; + } + m_userDisabledCallback = userDisabledCallback; + m_notificationCallback = notificationCallback; + m_userDisabledEvent = CreateEventA(NULL, FALSE, FALSE, (USER_DISABLED_EVENT + userId).c_str()); + if (!m_userDisabledEvent) + { + return false; + } + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + CloseHandle(m_userDisabledEvent); + m_userDisabledEvent = NULL; + throw std::runtime_error("Only one session allowed per user."); + } + m_notificationAvailableEvent = CreateEventA(NULL, FALSE, FALSE, (NOTIFICATION_AVAILABLE_EVENT + userId).c_str()); + if (!m_notificationAvailableEvent) + { + CloseHandle(m_userDisabledEvent); + m_userDisabledEvent = NULL; + return false; + } + m_shutdownEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!m_shutdownEvent) + { + CloseHandle(m_userDisabledEvent); + CloseHandle(m_notificationAvailableEvent); + m_userDisabledEvent = NULL; + m_notificationAvailableEvent = NULL; + return false; + } + m_running.store(true); + m_listenerThread = std::thread(&EventManager::run, this); + return true; +} + +/* +Function: shutdown +Description: Stops the listener thread and releases event resources. +Parameter: None +Return type: None +*/ +void EventManager::shutdown() +{ + if (!m_running.load()) + { + return; + } + m_running.store(false); + if (m_shutdownEvent) + { + SetEvent(m_shutdownEvent); + } + if (m_listenerThread.joinable()) + { + if (std::this_thread::get_id() != m_listenerThread.get_id()) + { + m_listenerThread.join(); + } + } + if (m_userDisabledEvent) + { + CloseHandle(m_userDisabledEvent); + m_userDisabledEvent = NULL; + } + if (m_notificationAvailableEvent) + { + CloseHandle(m_notificationAvailableEvent); + m_notificationAvailableEvent = NULL; + } + if (m_shutdownEvent) + { + CloseHandle(m_shutdownEvent); + m_shutdownEvent = NULL; + } +} + +/* +Function: run +Description: Waits for and dispatches user-related events. +Parameter: None +Return type: void +*/ +void EventManager::run() +{ + HANDLE handles[3]; + handles[0] = m_userDisabledEvent; + handles[1] = m_notificationAvailableEvent; + handles[2] = m_shutdownEvent; + while (m_running.load()) + { + DWORD result = WaitForMultipleObjects(3, handles, FALSE, INFINITE); + switch (result) + { + case WAIT_OBJECT_0: + try + { + if (m_userDisabledCallback) + { + m_userDisabledCallback(); + } + } + catch (const std::exception& exception) + { + std::cout << exception.what() << std::endl; + } + break; + case WAIT_OBJECT_0 + 1: + try + { + if (m_notificationCallback) + { + m_notificationCallback(); + } + } + catch (const std::exception& exception) + { + std::cout << exception.what() << std::endl; + } + break; + case WAIT_OBJECT_0 + 2: + return; + default: + break; + } + } +} + +/* +Function: sendUserDisabledEvent +Description: Publishes a user disabled event for a specific user. +Parameter: const std::string& userId - target user identifier +Return type: void +*/ +void EventManager::sendUserDisabledEvent(const std::string& userId) +{ + HANDLE eventHandle = CreateEventA(NULL, FALSE, FALSE, (USER_DISABLED_EVENT + userId).c_str()); + if (!eventHandle) + { + return; + } + SetEvent(eventHandle); + CloseHandle(eventHandle); +} + +/* +Function: sendNotificationAvailableEvent +Description: Publishes a notification available event for a specific + user. +Parameter: const std::string& userId - target user identifier +Return type: void +*/ +void EventManager::sendNotificationAvailableEvent(const std::string& userId) +{ + HANDLE eventHandle = CreateEventA(NULL, FALSE, FALSE, (NOTIFICATION_AVAILABLE_EVENT + userId).c_str()); + if (!eventHandle) + { + return; + } + SetEvent(eventHandle); + CloseHandle(eventHandle); +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.h new file mode 100644 index 0000000..01de6c4 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/events/EventManager.h @@ -0,0 +1,37 @@ +/* +File: EventManager.h +Description: Header file declaring the EventManager class, which manages + user-specific interprocess events for user disable and + notification availability updates. +Author: Trenser +Date:15-Jun-2026 +*/ + +#pragma once + +#include +#include +#include +#include +#include + +class EventManager +{ +private: + HANDLE m_userDisabledEvent; + HANDLE m_notificationAvailableEvent; + HANDLE m_shutdownEvent; + std::atomic m_running; + std::thread m_listenerThread; + std::function m_userDisabledCallback; + std::function m_notificationCallback; + void run(); + +public: + EventManager(); + ~EventManager(); + bool initialize(const std::string& userId, std::function userDisabledCallback, std::function notificationCallback); + void shutdown(); + static void sendUserDisabledEvent(const std::string& userId); + static void sendNotificationAvailableEvent(const std::string& userId); +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/FileHeader.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/FileHeader.h similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/FileHeader.h rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/FileHeader.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/MappingInfo.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/MappingInfo.h similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/MappingInfo.h rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/MappingInfo.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/RecordState.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/RecordState.h similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/RecordState.h rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/RecordState.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SerializedRecords.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SerializedRecords.h similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SerializedRecords.h rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SerializedRecords.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.cpp similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.cpp rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.cpp diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.h similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/SharedMemory.h rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/TrackedRecord.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/TrackedRecord.h similarity index 100% rename from Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/sharedmemory/TrackedRecord.h rename to Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/TrackedRecord.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp index 71d66b6..f573da9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp @@ -14,6 +14,9 @@ Date:19-May-2026 #include "DataStoreLockGuard.h" User* AuthenticationManagementService::m_authenticatedUser = nullptr; +EventManager AuthenticationManagementService::m_eventManager; +HANDLE AuthenticationManagementService::m_accountDisabledEvent = NULL; +HANDLE AuthenticationManagementService::m_notificationsAvailableEvent = NULL; /* Function: login @@ -37,6 +40,22 @@ bool AuthenticationManagementService::login(const std::string& username, const s if (password == user->getPassword()) { m_authenticatedUser = user; + m_eventManager.initialize( + user->getId(), + []() + { + if (m_accountDisabledEvent) + { + SetEvent(m_accountDisabledEvent); + } + }, + []() + { + if (m_notificationsAvailableEvent) + { + SetEvent(m_notificationsAvailableEvent); + } + }); return true; } return false; @@ -65,7 +84,10 @@ Return type: void */ void AuthenticationManagementService::logout() { + m_eventManager.shutdown(); m_authenticatedUser = nullptr; + m_accountDisabledEvent = NULL; + m_notificationsAvailableEvent = NULL; } /* @@ -92,3 +114,17 @@ void AuthenticationManagementService::changePassword(const std::string& newPassw trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; m_dataStore.saveUsers(); } + +/* +Function: registerEvents +Description: Registers menu event handles used to notify the active + menu of account disable and notification events. +Parameter: HANDLE accountDisabledEvent - account disabled event handle + HANDLE notificationAvailableEvent - notification event handle +Return type: void +*/ +void AuthenticationManagementService::registerEvents(HANDLE accountDisabledEvent, HANDLE notifictionAvailableEvent) +{ + m_accountDisabledEvent = accountDisabledEvent; + m_notificationsAvailableEvent = notifictionAvailableEvent; +} diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h index 47266a1..d512892 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h @@ -9,6 +9,8 @@ Date:19-May-2026 #pragma once #include +#include +#include "EventManager.h" #include "DataStore.h" class User; @@ -17,6 +19,9 @@ class AuthenticationManagementService { private: static User* m_authenticatedUser; + static EventManager m_eventManager; + static HANDLE m_accountDisabledEvent; + static HANDLE m_notificationsAvailableEvent; DataStore& m_dataStore; public: AuthenticationManagementService() : m_dataStore(DataStore::getInstance()) {} @@ -24,4 +29,5 @@ public: void logout(); void changePassword(const std::string& newPassword); User* getAuthenticatedUser(); + void registerEvents(HANDLE accountDisabledEvent, HANDLE notificationAvailableEvent); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp index 8e17739..a007ac1 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -19,6 +19,7 @@ Date: 22-May-2026 #include "Utility.h" #include "Vector.h" #include "DataStoreLockGuard.h" +#include "EventManager.h" util::Map InventoryManagementService::m_observers{}; @@ -281,5 +282,6 @@ void InventoryManagementService::sendNotification(User* user, const std::string& auto& trackedNotificationsMap = m_dataStore.getNotifications(); trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); m_dataStore.saveNotifications(); + EventManager::sendNotificationAvailableEvent(user->getId()); } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index dc42a5b..09bf1da 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -22,6 +22,7 @@ Date: 20-May-2026 #include "User.h" #include "Utility.h" #include "DataStoreLockGuard.h" +#include "EventManager.h" util::Map PaymentManagementService::m_observers{}; @@ -109,6 +110,7 @@ void PaymentManagementService::sendNotification(User* user, const std::string& t auto& trackedNotificationsMap = m_dataStore.getNotifications(); trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); m_dataStore.saveNotifications(); + EventManager::sendNotificationAvailableEvent(user->getId()); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index ed59380..cf438d0 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -27,6 +27,7 @@ Date:19-May-2026 #include "DataStoreLockGuard.h" #include "Utility.h" #include "DataStoreLockGuard.h" +#include "EventManager.h" /* Function: purchaseService @@ -199,6 +200,7 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t auto& trackedNotificationsMap = m_dataStore.getNotifications(); trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); m_dataStore.saveNotifications(); + EventManager::sendNotificationAvailableEvent(user->getId()); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index e923e1c..ab85936 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -22,6 +22,7 @@ Date:19-May-2026 #include "Utility.h" #include "TrackedRecord.h" #include "DataStoreLockGuard.h" +#include "EventManager.h" /* Function: ensureAdminExists @@ -267,30 +268,38 @@ void UserManagementService::removeUser(const std::string& userID) InventoryManagementService inventoryManagementService; PaymentManagementService paymentManagementService; ServiceManagementService serviceManagementService; - DataStoreLockGuard lock(m_dataStore); - auto& trackedUsersMap = m_dataStore.getUsers(); - int index = trackedUsersMap.find(userID); - if (index != -1) + std::string removedUserID; { - User* user = trackedUsersMap.getValueAt(index).data; - if (user != nullptr) + DataStoreLockGuard lock(m_dataStore); + auto& trackedUsersMap = m_dataStore.getUsers(); + int index = trackedUsersMap.find(userID); + if (index != -1) { - if (user->getUserType() == util::UserType::CUSTOMER) + User* user = trackedUsersMap.getValueAt(index).data; + if (user != nullptr) { - serviceManagementService.cancelCustomerServiceBookings(userID); + if (user->getUserType() == util::UserType::CUSTOMER) + { + serviceManagementService.cancelCustomerServiceBookings(userID); + } + if (user->getUserType() == util::UserType::TECHNICIAN) + { + serviceManagementService.cancelTechnicianJobs(userID); + } + inventoryManagementService.detach(user); + paymentManagementService.detach(user); + serviceManagementService.detach(user); + user->setState(util::State::INACTIVE); + trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; + removedUserID = user->getId(); + m_dataStore.saveUsers(); } - if (user->getUserType() == util::UserType::TECHNICIAN) - { - serviceManagementService.cancelTechnicianJobs(userID); - } - inventoryManagementService.detach(user); - paymentManagementService.detach(user); - serviceManagementService.detach(user); - user->setState(util::State::INACTIVE); - trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED; - m_dataStore.saveUsers(); } } + if (!removedUserID.empty()) + { + EventManager::sendUserDisabledEvent(removedUserID); + } } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp index 76a1901..68ec663 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp @@ -30,10 +30,16 @@ Return type: void */ void AdminMenu::showMenu() { + startEventListener(); while (true) { try { + if (!m_isMenuActive) + { + logout(); + break; + } int choice; util::clear(); std::cout << "Admin Menu" @@ -68,6 +74,7 @@ void AdminMenu::showMenu() util::pressEnter(); } } + stopEventListener(); } /* @@ -78,6 +85,11 @@ Return type: bool - true if menu continues, false if logout */ bool AdminMenu::handleOperation(int choice) { + if (!m_isMenuActive) + { + logout(); + return false; + } switch (choice) { case 1: @@ -141,6 +153,19 @@ bool AdminMenu::handleOperation(int choice) return true; } +/* +Function: handleNotificationEvent +Description: Retrieves and displays the latest notification for the + currently logged in admin. +Parameter: None +Return type: void +*/ +void AdminMenu::handleNotificationEvent() +{ + auto notifications = m_controller.getNotifications(); + displayNewNotification(notifications); +} + /* Function: logout Description: Logs out the currently authenticated admin user. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.h index bdffcb6..f9aa415 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.h @@ -9,12 +9,13 @@ Date:19-May-2026 #pragma once #include "Controller.h" +#include "Menu.h" -class AdminMenu +class AdminMenu : public Menu { private: - Controller m_controller; bool handleOperation(int choice); + void handleNotificationEvent() override; public: void showMenu(); void logout(); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp index 0d60f97..3932fba 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp @@ -32,10 +32,17 @@ Return type: void */ void CustomerMenu::showMenu() { + + startEventListener(); while (true) { try { + if (!m_isMenuActive) + { + logout(); + break; + } int choice; util::clear(); std::cout << "Customer Menu" @@ -62,6 +69,7 @@ void CustomerMenu::showMenu() util::pressEnter(); } } + stopEventListener(); } /* @@ -72,6 +80,11 @@ Return type: bool - true if menu continues, false if logout */ bool CustomerMenu::handleOperation(int choice) { + if (!m_isMenuActive) + { + logout(); + return false; + } switch (choice) { case 1: @@ -111,6 +124,19 @@ bool CustomerMenu::handleOperation(int choice) return true; } +/* +Function: handleNotificationEvent +Description: Retrieves and displays the latest notification for the + currently logged in admin. +Parameter: None +Return type: void +*/ +void CustomerMenu::handleNotificationEvent() +{ + auto notifications = m_controller.getNotifications(); + displayNewNotification(notifications); +} + /* Function: logout Description: Logs out the currently authenticated customer user. diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h index d491720..c619107 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.h @@ -9,13 +9,14 @@ Date:19-May-2026 */ #pragma once +#include "Menu.h" #include "Controller.h" -class CustomerMenu +class CustomerMenu : public Menu { private: - Controller m_controller; bool handleOperation(int choice); + void handleNotificationEvent(); public: void showMenu(); void logout(); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp new file mode 100644 index 0000000..2573c0e --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp @@ -0,0 +1,144 @@ +/* +File: Menu.cpp +Description: Implementation file containing common menu event listener + functionality, account disable handling, and notification + event dispatching for all menu types. +Author: Trenser +Date:16-Jun-2026 +*/ + +#include "Menu.h" + +/* +Function: Menu +Description: Constructs a Menu object and initializes event handles + and menu state. +Parameter: None +Return type: None +*/ +Menu::Menu() + : + m_isMenuActive(false), + m_accountDisabledEvent(NULL), + m_notificationAvailableEvent(NULL), + m_shutdownEvent(NULL) {} + +/* +Function: ~Menu +Description: Destroys the Menu object and performs event listener + cleanup. +Parameter: None +Return type: None +*/ +Menu::~Menu() +{ + stopEventListener(); +} + +/* +Function: startEventListener +Description: Creates menu event handles, registers them with the + authentication service, and starts the event listener + thread. +Parameter: None +Return type: void +*/ +void Menu::startEventListener() +{ + if (m_isMenuActive.load()) + { + return; + } + m_isMenuActive.store(true); + m_accountDisabledEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + m_notificationAvailableEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + m_shutdownEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + m_controller.registerEvents(m_accountDisabledEvent, m_notificationAvailableEvent); + m_eventListenerThread = std::thread(&Menu::eventListenerLoop, this); +} + +/* +Function: eventListenerLoop +Description: Waits for account disabled, notification available, + and shutdown events and dispatches them to the + appropriate handlers. +Parameter: None +Return type: void +*/ +void Menu::eventListenerLoop() +{ + HANDLE handles[3]; + handles[0] = m_accountDisabledEvent; + handles[1] = m_notificationAvailableEvent; + handles[2] = m_shutdownEvent; + while (m_isMenuActive.load()) + { + DWORD result = WaitForMultipleObjects(3, handles, FALSE, INFINITE); + switch (result) + { + case WAIT_OBJECT_0: + handleAccountDisabledEvent(); + break; + case WAIT_OBJECT_0 + 1: + handleNotificationEvent(); + break; + case WAIT_OBJECT_0 + 2: + return; + } + } +} + +/* +Function: stopEventListener +Description: Stops the event listener thread and releases all + associated event handles. +Parameter: None +Return type: void +*/ +void Menu::stopEventListener() +{ + m_isMenuActive.store(false); + if (m_shutdownEvent) + { + SetEvent(m_shutdownEvent); + } + if (m_eventListenerThread.joinable()) + { + m_eventListenerThread.join(); + } + if (m_accountDisabledEvent) + { + CloseHandle(m_accountDisabledEvent); + } + if (m_notificationAvailableEvent) + { + CloseHandle(m_notificationAvailableEvent); + } + if (m_shutdownEvent) + { + CloseHandle(m_shutdownEvent); + } + m_accountDisabledEvent = NULL; + m_notificationAvailableEvent = NULL; + m_shutdownEvent = NULL; +} + +/* +Function: handleAccountDisabledEvent +Description: Handles an account disabled event by marking the menu + inactive and notifying the user. +Parameter: None +Return type: void +*/ +void Menu::handleAccountDisabledEvent() +{ + m_isMenuActive.store(false); + MessageBoxA( + GetConsoleWindow(), + "Your account has been disabled.", + "Account Disabled", + MB_OK | + MB_ICONWARNING | + MB_SETFOREGROUND | + MB_TOPMOST); +} diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.h new file mode 100644 index 0000000..146efa8 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.h @@ -0,0 +1,33 @@ +/* +File: Menu.h +Description: Base class providing common event listener functionality + for all menu implementations. +Author: Trenser +Date:16-Jun-2026 +*/ + +#pragma once + +#include +#include +#include +#include "Controller.h" + +class Menu +{ +protected: + Controller m_controller; + std::atomic m_isMenuActive; + HANDLE m_accountDisabledEvent; + HANDLE m_notificationAvailableEvent; + HANDLE m_shutdownEvent; + std::thread m_eventListenerThread; + void startEventListener(); + void stopEventListener(); + void eventListenerLoop(); + void handleAccountDisabledEvent(); + virtual void handleNotificationEvent() = 0; +public: + Menu(); + virtual ~Menu(); +}; \ 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 c6ebaa6..0d0f695 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -1407,4 +1407,38 @@ inline std::string selectComboPackage(util::Map notifications - + collection of notifications +Return type: void +*/ +inline void displayNewNotification(util::Vector notifications) +{ + const Notification* notification = nullptr; + size_t numberOfNotifications = notifications.getSize(); + for (int index = 0; index < numberOfNotifications; index++) + { + if (!notification) + { + notification = notifications[index]; + } + else + { + if (notification->getId() < notifications[index]->getId()) + { + notification = notifications[index]; + } + } + } + MessageBoxA( + GetConsoleWindow(), + notification->getMessage().c_str(), + notification->getTitle().c_str(), + MB_OK | + MB_ICONINFORMATION); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp index 8ff3e12..4b090d8 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp @@ -27,10 +27,16 @@ Returns: */ void TechnicianMenu::showMenu() { + startEventListener(); while (true) { try { + if (!m_isMenuActive) + { + logout(); + break; + } int choice; util::clear(); std::cout << "Technician Menu" @@ -52,6 +58,7 @@ void TechnicianMenu::showMenu() util::pressEnter(); } } + stopEventListener(); } /* @@ -62,6 +69,11 @@ Return type: bool - true if menu continues, false if logout */ bool TechnicianMenu::handleOperation(int choice) { + if (!m_isMenuActive) + { + logout(); + return false; + } switch (choice) { case 1: @@ -86,6 +98,19 @@ bool TechnicianMenu::handleOperation(int choice) return true; } +/* +Function: handleNotificationEvent +Description: Retrieves and displays the latest notification for the + currently logged in admin. +Parameter: None +Return type: void +*/ +void TechnicianMenu::handleNotificationEvent() +{ + auto notifications = m_controller.getNotifications(); + displayNewNotification(notifications); +} + /* Function: displayJobs Description: Displays all Jobs assigned to a Technician diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h index 2d118a0..b00c1f3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.h @@ -9,12 +9,13 @@ Date:19-May-2026 #pragma once #include "Controller.h" +#include "Menu.h" -class TechnicianMenu +class TechnicianMenu : public Menu { private: - Controller m_controller; bool handleOperation(int choice); + void handleNotificationEvent(); public: void showMenu(); void displayJobs(); From ae37b83cbcdcfbc67143bb7f47740a10f30cc0b8 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Tue, 16 Jun 2026 11:29:00 +0530 Subject: [PATCH 21/39] Implement review fixes --- .../services/AuthenticationManagementService.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp index f573da9..56137e5 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp @@ -123,8 +123,8 @@ Parameter: HANDLE accountDisabledEvent - account disabled event handle HANDLE notificationAvailableEvent - notification event handle Return type: void */ -void AuthenticationManagementService::registerEvents(HANDLE accountDisabledEvent, HANDLE notifictionAvailableEvent) +void AuthenticationManagementService::registerEvents(HANDLE accountDisabledEvent, HANDLE notificationAvailableEvent) { m_accountDisabledEvent = accountDisabledEvent; - m_notificationsAvailableEvent = notifictionAvailableEvent; + m_notificationsAvailableEvent = notificationAvailableEvent; } From 78e6ae2fec5b7bcdd5348e302e835ef7639507cc Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 17 Jun 2026 11:30:15 +0530 Subject: [PATCH 22/39] Fix cache refresh from overwriting modified records Changes: - Added a check for RecordState::MODIFIED in DataStore::refreshCache(). - Preserved modified cache records during refresh instead of overwriting them with datastore values. - Discarded refreshed records when a corresponding cached record has pending changes. Fixes #2077 --- .../Trenser.VehicleServiceSystem/datastores/DataStore.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 021d831..03eb0eb 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -220,6 +220,12 @@ void DataStore::refreshCache(util::Map>& cac if (oldIndex != -1) { TrackedRecord& oldRecord = oldCache.getValueAt(oldIndex); + if (oldRecord.state == RecordState::MODIFIED) + { + delete refreshedRecord.data; + cache.insert(id, oldRecord); + continue; + } *oldRecord.data = *refreshedRecord.data; oldRecord.slotIndex = refreshedRecord.slotIndex; oldRecord.state = refreshedRecord.state; From b011f600cff399d3dc12e69739d64d71e0e45701 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 17 Jun 2026 14:07:38 +0530 Subject: [PATCH 23/39] Fix notification overwrite caused by stale notification cache Changes: - Retrieved the notifications map before creating a new notification in sendNotification(). - Ensured the notification cache is refreshed before generating a notification ID. - Prevented newly created notifications from overwriting notifications added by other operations prior to save. Fixes #2079 --- .../services/InventoryManagementService.cpp | 2 +- .../services/PaymentManagementService.cpp | 2 +- .../services/ServiceManagementService.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp index a007ac1..f906431 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -270,6 +270,7 @@ void InventoryManagementService::sendNotification(User* user, const std::string& { return; } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); Notification* notification = Factory::getObject( user->getId(), title, @@ -279,7 +280,6 @@ void InventoryManagementService::sendNotification(User* user, const std::string& { throw std::runtime_error("Failed to create notification"); } - auto& trackedNotificationsMap = m_dataStore.getNotifications(); trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); m_dataStore.saveNotifications(); EventManager::sendNotificationAvailableEvent(user->getId()); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index 09bf1da..dad4421 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -98,6 +98,7 @@ void PaymentManagementService::sendNotification(User* user, const std::string& t { return; } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); Notification* notification = Factory::getObject( user->getId(), title, @@ -107,7 +108,6 @@ void PaymentManagementService::sendNotification(User* user, const std::string& t { throw std::runtime_error("Failed to create notification"); } - auto& trackedNotificationsMap = m_dataStore.getNotifications(); trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); m_dataStore.saveNotifications(); EventManager::sendNotificationAvailableEvent(user->getId()); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index cf438d0..e703453 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -188,6 +188,7 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t { return; } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); Notification* notification = Factory::getObject( user->getId(), title, @@ -197,7 +198,6 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t { throw std::runtime_error("Failed to create notification"); } - auto& trackedNotificationsMap = m_dataStore.getNotifications(); trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); m_dataStore.saveNotifications(); EventManager::sendNotificationAvailableEvent(user->getId()); From 1179f92849af361bd9e7f88d012683f3f1e8b30a Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 17 Jun 2026 14:20:20 +0530 Subject: [PATCH 24/39] Notification Dialog Does Not Indicate Intended Recipient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Updated AdminMenu::handleNotificationEvent, CustomerMenu::handleNotificationEvent, and TechnicianMenu::handleNotificationEvent to retrieve the authenticated user and pass their name to displayNewNotification(). - Modified Menu::handleAccountDisabledEvent to append the authenticated user’s name to the message box title when available. - Refactored displayNewNotification() in MenuHelper.h to accept the authenticated user’s name as an additional parameter. - Enhanced notification display logic to append the user’s name to the notification title when present. - Added null checks to ensure safe handling when no authenticated user exists. FIxes #2080 --- .../views/AdminMenu.cpp | 8 ++++++- .../views/CustomerMenu.cpp | 8 ++++++- .../views/Menu.cpp | 8 ++++++- .../views/MenuHelper.h | 23 +++++++++++++------ .../views/TechnicianMenu.cpp | 8 ++++++- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp index 68ec663..71882ba 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp @@ -163,7 +163,13 @@ Return type: void void AdminMenu::handleNotificationEvent() { auto notifications = m_controller.getNotifications(); - displayNewNotification(notifications); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string name; + if (authenticatedUser) + { + name = authenticatedUser->getName(); + } + displayNewNotification(notifications, name); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp index 3932fba..a9d7990 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp @@ -134,7 +134,13 @@ Return type: void void CustomerMenu::handleNotificationEvent() { auto notifications = m_controller.getNotifications(); - displayNewNotification(notifications); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string name; + if (authenticatedUser) + { + name = authenticatedUser->getName(); + } + displayNewNotification(notifications, name); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp index 2573c0e..e5881ff 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp @@ -133,10 +133,16 @@ Return type: void void Menu::handleAccountDisabledEvent() { m_isMenuActive.store(false); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string messageTitle = "Account Disabled"; + if (authenticatedUser) + { + messageTitle += " - " + authenticatedUser->getName(); + } MessageBoxA( GetConsoleWindow(), "Your account has been disabled.", - "Account Disabled", + messageTitle.c_str(), MB_OK | MB_ICONWARNING | MB_SETFOREGROUND | diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 0d0f695..a92bead 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -1415,9 +1415,10 @@ Description: Displays the most recent notification from the supplied notification collection. Parameter: util::Vector notifications - collection of notifications + const std::string& - The name of the user currently authenticated with the system Return type: void */ -inline void displayNewNotification(util::Vector notifications) +inline void displayNewNotification(util::Vector notifications, const std::string& name) { const Notification* notification = nullptr; size_t numberOfNotifications = notifications.getSize(); @@ -1435,10 +1436,18 @@ inline void displayNewNotification(util::Vector notificatio } } } - MessageBoxA( - GetConsoleWindow(), - notification->getMessage().c_str(), - notification->getTitle().c_str(), - MB_OK | - MB_ICONINFORMATION); + if (notification) + { + std::string messageTitle = notification->getTitle(); + if (!name.empty()) + { + messageTitle += " - " + name; + } + MessageBoxA( + GetConsoleWindow(), + notification->getMessage().c_str(), + messageTitle.c_str(), + MB_OK | + MB_ICONINFORMATION); + } } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp index 4b090d8..ee4576c 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp @@ -108,7 +108,13 @@ Return type: void void TechnicianMenu::handleNotificationEvent() { auto notifications = m_controller.getNotifications(); - displayNewNotification(notifications); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string name; + if (authenticatedUser) + { + name = authenticatedUser->getName(); + } + displayNewNotification(notifications, name); } /* From 974d4efe0227c22da598ae7515c992e15734aa34 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Wed, 17 Jun 2026 14:47:37 +0530 Subject: [PATCH 25/39] Fix Service Booking Creation Is Not Synchronized Between Processes Changes: - Updated ServiceManagementService::purchaseService() to persist service bookings immediately after creation using m_dataStore.saveServiceBookings(). - Updated ServiceManagementService::purchaseComboPackage() to persist combo package bookings immediately after creation using m_dataStore.saveServiceBookings(). - Changed DataStore::getJobCards() to use a reference for tracked bookings to ensure consistent linkage and prevent overwriting. Fixes #2074 --- .../Trenser.VehicleServiceSystem/datastores/DataStore.cpp | 2 +- .../services/ServiceManagementService.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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 cf438d0..2949778 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -72,6 +72,7 @@ void ServiceManagementService::purchaseService(const util::Vector& std::string title = "Service Booking succeeded"; std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId(); sendNotification(authenticatedUser, title, message); + m_dataStore.saveServiceBookings(); } /* @@ -112,6 +113,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(); } util::Map ServiceManagementService::m_observers{}; From 9d166362a7a75aba5a1ff305e5e06cf9367a184f Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 17 Jun 2026 15:17:55 +0530 Subject: [PATCH 26/39] Latest Notification Is Determined Using String Comparison Changes: - Added #include "StringHelper.h" to MenuHelper.h for utility functions. - Updated displayNewNotification() to compare notification IDs using util::extractNumber() instead of raw string comparison. - Ensured numeric ordering of notifications by extracting and comparing integer values from IDs. - Preserved existing notification selection logic while improving accuracy of ID comparisons. Fixes #2081 --- .../Trenser.VehicleServiceSystem/views/MenuHelper.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index a92bead..c87707a 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -28,6 +28,7 @@ Date: 21-May-2026 #include "Utility.h" #include "Validator.h" #include "Vector.h" +#include "StringHelper.h" /* Function: displayAllServices @@ -1430,7 +1431,7 @@ inline void displayNewNotification(util::Vector notificatio } else { - if (notification->getId() < notifications[index]->getId()) + if (util::extractNumber(notification->getId()) < util::extractNumber(notifications[index]->getId())) { notification = notifications[index]; } @@ -1450,4 +1451,4 @@ inline void displayNewNotification(util::Vector notificatio MB_OK | MB_ICONINFORMATION); } -} \ No newline at end of file +} From 4243f4e43f4d84ac02d1d5c9a19c3d1225a87a8b Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Wed, 17 Jun 2026 15:29:46 +0530 Subject: [PATCH 27/39] Fix Inventory Quantity Is Not Decremented When Job Card Is Created Changes: - Added retrieval of tracked service bookings and inventory items in ServiceManagementService::createJobCard(). - Validated service booking ID and inventory item indices before proceeding with job card creation. - Decremented inventory item quantities when job cards are created and marked corresponding tracked inventory records as MODIFIED. - Updated tracked service booking state to MODIFIED when technician is assigned and job status changes. - Persisted changes by saving job cards, service bookings, and inventory items to the datastore. Fixes #2075 --- .../services/ServiceManagementService.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 2949778..f20c3d6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -596,6 +596,14 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); auto& currentTrackedJobCards = m_dataStore.getJobCards(); + 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); if (currentBooking == nullptr) { throw std::runtime_error("Service Booking not available"); @@ -624,10 +632,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); @@ -636,6 +652,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()); @@ -652,6 +669,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(); } /* From 82164e42c35196d2cf239d9b1c0f44e02598d4e2 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 17 Jun 2026 16:47:15 +0530 Subject: [PATCH 28/39] Stop disabled users from performing operations after account deactivation Changes: - Added authorization state tracking to authenticated sessions. - Revoked authorization immediately when an account-disabled event is received. - Added authorization validation before protected service operations. - Cleared authorization state on logout. Fixes #2078 --- .../AuthenticationManagementService.cpp | 23 +++++++++++++++++++ .../AuthenticationManagementService.h | 2 ++ .../services/InventoryManagementService.cpp | 6 +++++ .../services/PaymentManagementService.cpp | 7 +++++- .../services/ServiceManagementService.cpp | 14 +++++++++++ .../services/UserManagementService.cpp | 5 ++++ 6 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp index 56137e5..dd80cd9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp @@ -14,10 +14,29 @@ Date:19-May-2026 #include "DataStoreLockGuard.h" User* AuthenticationManagementService::m_authenticatedUser = nullptr; +bool AuthenticationManagementService::m_isAuthorized = false; EventManager AuthenticationManagementService::m_eventManager; HANDLE AuthenticationManagementService::m_accountDisabledEvent = NULL; HANDLE AuthenticationManagementService::m_notificationsAvailableEvent = NULL; + +/* +Function: ensureAuthorization +Description: Verifies that a user is currently authenticated before allowing + access to a protected operation. Throws an exception if no + authorized user session exists. +Parameter: None +Return type: void +Throws: std::runtime_error - if the user is not authorized +*/ +void AuthenticationManagementService::ensureAuthorization() +{ + if (!m_authenticatedUser || !m_isAuthorized) + { + throw std::runtime_error("You are not authorized to do this operation!"); + } +} + /* Function: login Description: Authenticates a user by checking the provided username and password @@ -40,12 +59,14 @@ bool AuthenticationManagementService::login(const std::string& username, const s if (password == user->getPassword()) { m_authenticatedUser = user; + m_isAuthorized = true; m_eventManager.initialize( user->getId(), []() { if (m_accountDisabledEvent) { + AuthenticationManagementService::m_isAuthorized = false; SetEvent(m_accountDisabledEvent); } }, @@ -86,6 +107,7 @@ void AuthenticationManagementService::logout() { m_eventManager.shutdown(); m_authenticatedUser = nullptr; + m_isAuthorized = false; m_accountDisabledEvent = NULL; m_notificationsAvailableEvent = NULL; } @@ -99,6 +121,7 @@ Return type: void */ void AuthenticationManagementService::changePassword(const std::string& newPassword) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedUsersMap = m_dataStore.getUsers(); if (m_authenticatedUser == nullptr) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h index d512892..e927187 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h @@ -19,12 +19,14 @@ class AuthenticationManagementService { private: static User* m_authenticatedUser; + static bool m_isAuthorized; static EventManager m_eventManager; static HANDLE m_accountDisabledEvent; static HANDLE m_notificationsAvailableEvent; DataStore& m_dataStore; public: AuthenticationManagementService() : m_dataStore(DataStore::getInstance()) {} + static void ensureAuthorization(); bool login(const std::string& username, const std::string& password); void logout(); void changePassword(const std::string& newPassword); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp index f906431..6152fd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -14,6 +14,7 @@ Date: 22-May-2026 #include "Factory.h" #include "InventoryItem.h" #include "InventoryManagementService.h" +#include "AuthenticationManagementService.h" #include "Timestamp.h" #include "User.h" #include "Utility.h" @@ -102,6 +103,7 @@ Return type: void */ void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); InventoryItem* newItem = Factory::getObject(partName, quantity, price); @@ -118,6 +120,7 @@ Return type: void */ void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); int index = trackedInventoryItemMap.find(selectedItemId); @@ -144,6 +147,7 @@ Return type: util::Map */ util::Map InventoryManagementService::getInventoryItems() { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); auto inventoryMap = util::getObjects(trackedInventoryItemMap); @@ -158,6 +162,7 @@ Return type: void */ void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); int index = trackedInventoryItemMap.find(inventoryItemID); @@ -183,6 +188,7 @@ Return type: InventoryItem* */ InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); int index = trackedInventoryItemMap.find(inventoryItemID); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index dad4421..b09b9bf 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -15,6 +15,7 @@ Date: 20-May-2026 #include "Invoice.h" #include "JobCard.h" #include "PaymentManagementService.h" +#include "AuthenticationManagementService.h" #include "DataStoreLockGuard.h" #include "Service.h" #include "ServiceBooking.h" @@ -233,6 +234,7 @@ Returns: */ util::Map PaymentManagementService::getInvoices(const std::string& customerID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& currentTrackedInvoices = m_dataStore.getInvoices(); util::Map currentUserInvoices; @@ -261,6 +263,7 @@ Throws: */ void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& currentTrackedInvoices = m_dataStore.getInvoices(); int invoiceIndex = currentTrackedInvoices.find(invoiceID); @@ -289,7 +292,7 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti } /* -Function: getAllInvoice +Function: getAllInvoices Description: Provides access to all invoices stored in the data store. Parameters: - none @@ -298,6 +301,7 @@ Returns: */ util::Map PaymentManagementService::getAllInvoices() { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); util::Map invoices; invoices = util::getObjects(m_dataStore.getInvoices()); @@ -317,6 +321,7 @@ Throws: */ void PaymentManagementService::confirmPayment(const std::string& invoiceID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& currentTrackedInvoices = m_dataStore.getInvoices(); int invoiceIndex = currentTrackedInvoices.find(invoiceID); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index e703453..da5eef6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -42,6 +42,7 @@ Return type: void */ void ServiceManagementService::purchaseService(const util::Vector& serviceIDs, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel) { + AuthenticationManagementService::ensureAuthorization(); AuthenticationManagementService m_authenticationManagementService; auto authenticatedUser = m_authenticationManagementService.getAuthenticatedUser(); if (authenticatedUser == nullptr) @@ -87,6 +88,7 @@ Return type: void */ void ServiceManagementService::purchaseComboPackage(const std::string& comboPackageID, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel) { + AuthenticationManagementService::ensureAuthorization(); AuthenticationManagementService m_authenticationManagementService; auto authenticatedUser = m_authenticationManagementService.getAuthenticatedUser(); if (authenticatedUser == nullptr) @@ -433,6 +435,7 @@ Return type: void */ void ServiceManagementService::createComboPackage(const std::string& packageName, const util::Vector& serviceIDsInNewCombo, double discountPercentage) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); if (packageName.empty()) { @@ -502,6 +505,7 @@ Return type: util::Map */ util::Map ServiceManagementService::getComboPackages() { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); util::Map comboPackages; comboPackages = util::getObjects(m_dataStore.getComboPackages()); @@ -516,6 +520,7 @@ Return type: void */ void ServiceManagementService::removeComboPackage(const std::string& comboPackageID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); bool removed = false; auto& trackedComboPackages = m_dataStore.getComboPackages(); @@ -548,6 +553,7 @@ Returns: */ util::Map ServiceManagementService::getServiceBookings() { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); util::Map serviceBookings; serviceBookings = util::getObjects(m_dataStore.getServiceBookings()); @@ -564,6 +570,7 @@ Returns: */ ServiceBooking* ServiceManagementService::getServiceBooking(const std::string& serviceID) { + AuthenticationManagementService::ensureAuthorization(); auto currentServiceBookings = getServiceBookings(); for (int iterator = 0; iterator < currentServiceBookings.getSize(); iterator++) { @@ -590,6 +597,7 @@ Throws: */ void ServiceManagementService::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); @@ -667,6 +675,7 @@ Throws: */ void ServiceManagementService::createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); util::Map currentServiceInventoryItems; auto& trackedInventoryItems = m_dataStore.getInventoryItems(); @@ -713,6 +722,7 @@ Returns: */ util::Map ServiceManagementService::getServices() { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); util::Map services; services = util::getObjects(m_dataStore.getServices()); @@ -731,6 +741,7 @@ Throws: */ void ServiceManagementService::removeService(const std::string& serviceID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& currentTrackedServices = m_dataStore.getServices(); auto& currentTrackedComboPackages = m_dataStore.getComboPackages(); @@ -778,6 +789,7 @@ Returns: */ util::Map ServiceManagementService::getServiceBookings(const std::string& customerID) { + AuthenticationManagementService::ensureAuthorization(); util::Map currentServiceBookings = getServiceBookings(); util::Map currentUserServiceBookings; if (currentServiceBookings.getSize() != 0) @@ -804,6 +816,7 @@ Returns: */ util::Map ServiceManagementService::getJobCards(const std::string& technicianID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedJobCards = m_dataStore.getJobCards(); util::Map technicianJobCards; @@ -858,6 +871,7 @@ Returns: */ void ServiceManagementService::updateJobStatus(const std::string& jobID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); AuthenticationManagementService authenticationManagementService; PaymentManagementService paymentManagementService; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index ab85936..0c3991d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -15,6 +15,7 @@ Date:19-May-2026 #include "Notification.h" #include "PaymentManagementService.h" #include "ServiceManagementService.h" +#include "AuthenticationManagementService.h" #include "User.h" #include "UserManagementService.h" #include "Vector.h" @@ -114,6 +115,7 @@ Return type: void */ void UserManagementService::updateUserDetails(const std::string& userID, const std::string& email, const std::string& phone) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedUsersMap = m_dataStore.getUsers(); auto usersMap = util::getObjects(trackedUsersMap); @@ -161,6 +163,7 @@ Throws: */ util::Vector UserManagementService::getUserNotifications(const std::string& userID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedUsersMap = m_dataStore.getUsers(); if (trackedUsersMap.find(userID) == -1) @@ -204,6 +207,7 @@ Throws: */ void UserManagementService::deleteNotification(const std::string& notificationID, const std::string& userID) { + AuthenticationManagementService::ensureAuthorization(); DataStoreLockGuard lock(m_dataStore); auto& trackedUsersMap = m_dataStore.getUsers(); auto& trackedNotificationsMap = m_dataStore.getNotifications(); @@ -265,6 +269,7 @@ Return type: void */ void UserManagementService::removeUser(const std::string& userID) { + AuthenticationManagementService::ensureAuthorization(); InventoryManagementService inventoryManagementService; PaymentManagementService paymentManagementService; ServiceManagementService serviceManagementService; From bb0d186b6268c6a80cbe357cfee47006342855a4 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Wed, 17 Jun 2026 17:01:34 +0530 Subject: [PATCH 29/39] Fix Completing a Job Throws an Exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Refactored ServiceManagementService::updateJobStatus() to use tracked job card references instead of raw pointers, ensuring consistent state updates. - Added proper null checks and error handling for current job retrieval to prevent unexpected termination. - Updated logic to mark tracked job records as MODIFIED when status transitions occur (STARTED → IN_PROGRESS, IN_PROGRESS → COMPLETED). - Simplified control flow and indentation for better readability and maintainability. Fixes #2076 --- .../datastores/DataStore.h | 5 ++ .../services/ServiceManagementService.cpp | 74 +++++++++---------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 021d831..7093599 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -220,6 +220,11 @@ void DataStore::refreshCache(util::Map>& cac if (oldIndex != -1) { TrackedRecord& oldRecord = oldCache.getValueAt(oldIndex); + if (oldRecord.state == RecordState::MODIFIED) + { + cache.insert(id, oldRecord); + continue; + } *oldRecord.data = *refreshedRecord.data; oldRecord.slotIndex = refreshedRecord.slotIndex; oldRecord.state = refreshedRecord.state; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index f20c3d6..2518859 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -880,44 +880,44 @@ Returns: void ServiceManagementService::updateJobStatus(const std::string& jobID) { 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) @@ -931,15 +931,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 From 468106952f9894c67234df6d05c468ddb5fce4ac Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Wed, 17 Jun 2026 17:39:45 +0530 Subject: [PATCH 30/39] Notify admins when new service bookings are created Changes: - Added notifyAllAdmins() helper in ServiceManagementService. - Sent notifications to all administrators when a customer places a service booking. - Sent notifications to all administrators when a customer purchases a combo package. - Included the generated Service Booking ID in admin notifications. Fixes #2100 --- .../services/ServiceManagementService.cpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index da5eef6..a4d72bd 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -29,6 +29,31 @@ Date:19-May-2026 #include "DataStoreLockGuard.h" #include "EventManager.h" +/* +Function: notifyAllAdmins +Description: Sends a notification to all users with the ADMIN role. + Iterates through the provided user collection and delivers + the specified notification to each administrator using the + ServiceManagementService notification mechanism. +Parameter: const std::string& title - notification title +Parameter: const std::string& message - notification message +Parameter: const util::Map>& users - collection of tracked users +Parameter: ServiceManagementService* serviceManagementService - service used to dispatch notifications +Return type: void +*/ +static void notifyAllAdmins(const std::string& title, const std::string& message, const util::Map>& users, ServiceManagementService* serviceManagementService) +{ + int numberOfUsers = users.getSize(); + for (int index = 0; index < numberOfUsers; index++) + { + User* user = users.getValueAt(index).data; + if (user->getUserType() == util::UserType::ADMIN) + { + serviceManagementService->sendNotification(user, title, message); + } + } +} + /* Function: purchaseService Description: Creates a new service booking for the authenticated user. Validates @@ -73,6 +98,7 @@ void ServiceManagementService::purchaseService(const util::Vector& std::string title = "Service Booking succeeded"; std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId(); sendNotification(authenticatedUser, title, message); + notifyAllAdmins("New Service Order Available", "A new service order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this); } /* @@ -114,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); + notifyAllAdmins("New Combo Package Order Available", "A new combo package order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this); } util::Map ServiceManagementService::m_observers{}; From 1651dfeafe337d62554ee7155fe2eb61599d82c9 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Wed, 17 Jun 2026 18:10:12 +0530 Subject: [PATCH 31/39] Service Can Still Be Purchased After Required Inventory Item Is Removed Changes: - Added m_dataStore.saveServiceBookings() in ServiceManagementService::purchaseService() to persist bookings immediately after successful purchase. - Enhanced AdminMenu::removeInventoryItem() to also remove all services that depend on the removed inventory item, ensuring consistency between inventory and service availability. - Updated selectServiceFromServices() in MenuHelper.h to skip services if any of their required inventory items have a quantity less than 1, preventing users from selecting unavailable services. - Introduced inventory depletion checks before inserting services into the active services map, improving reliability of service selection. Fixes #2082 --- .../services/ServiceManagementService.cpp | 1 + .../views/AdminMenu.cpp | 17 +++++++++++++++++ .../views/MenuHelper.h | 15 +++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index cf438d0..879ed73 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -72,6 +72,7 @@ void ServiceManagementService::purchaseService(const util::Vector& std::string title = "Service Booking succeeded"; std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId(); sendNotification(authenticatedUser, title, message); + m_dataStore.saveServiceBookings(); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp index 71882ba..62c64cd 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp @@ -338,6 +338,23 @@ void AdminMenu::removeInventoryItem() std::string selectedItemId = selectedItem->getId(); m_controller.removeInventoryItem(selectedItemId); std::cout << "Item " << selectedItem->getPartName() << " removed successfully." << std::endl; + const util::Map& listOfService = m_controller.getServices(); + for (int serviceIndex = 0; serviceIndex < listOfService.getSize(); serviceIndex++) + { + const Service* service = listOfService.getValueAt(serviceIndex); + if (!service) + { + continue; + } + const util::Map& requiredItems = service->getRequiredInventoryItems(); + if (requiredItems.find(selectedItemId) != -1) + { + m_controller.removeService(service->getId()); + std::cout << "Service " << service->getName() + << " removed as the item " + << selectedItem->getPartName() << " required for the service has been removed." << std::endl; + } + } } } util::pressEnter(); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index c87707a..33cd5bd 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -1057,6 +1057,21 @@ inline const Service* selectServiceFromServices(const util::Map& requiredItems = currentService->getRequiredInventoryItems(); + for (int itemIndex = 0; itemIndex < requiredItems.getSize(); itemIndex++) + { + const InventoryItem* item = requiredItems.getValueAt(itemIndex); + if (!item || item->getQuantity() < 1) + { + hasDepletedItem = true; + break; + } + } + if (hasDepletedItem) + { + continue; + } activeServicesMap.insert(currentIndex, currentService); double partsCost = util::calculatePartsCost(currentService); std::cout << std::left From b45463a66d43ead2ab964cc04f04f1cc54dc4b3c Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Wed, 17 Jun 2026 20:48:54 +0530 Subject: [PATCH 32/39] Fix Cancel Service Booking Functionality Changes: - Added Controller::removeServiceBooking() with proper documentation and delegation to ServiceManagementService. - Implemented ServiceManagementService::removeServiceBooking() to handle cancellation of pending bookings, enforce status validation, send notifications, and persist changes safely. - Updated CustomerMenu to include a "Cancel Service Booking" option in the menu, wired it into handleOperation, and implemented CustomerMenu::cancelServiceBooking() for user interaction. - Ensured consistent declarations in Controller.h, ServiceManagementService.h, and CustomerMenu.h. Fixes #2105 --- .../controllers/Controller.cpp | 12 ++++ .../controllers/Controller.h | 1 + .../services/ServiceManagementService.cpp | 60 +++++++++++++++++- .../services/ServiceManagementService.h | 1 + .../views/CustomerMenu.cpp | 62 ++++++++++++++++--- .../views/CustomerMenu.h | 3 +- 6 files changed, 128 insertions(+), 11 deletions(-) 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/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 2518859..172cda4 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" /* @@ -595,6 +594,10 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const DataStoreLockGuard lock(m_dataStore); UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); + if (currentBooking->getStatus() == util::ServiceJobStatus::CANCELLED) + { + throw std::runtime_error("Cannot create job card. Service Booking was cancelled!"); + } auto& currentTrackedJobCards = m_dataStore.getJobCards(); auto& currentTrackedInventoryItems = m_dataStore.getInventoryItems(); auto& currentTrackedServiceBookings = m_dataStore.getServiceBookings(); @@ -789,6 +792,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. 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 3932fba..7956c17 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: @@ -327,6 +331,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 From ca0e277586ba06db98bac47cf38b88a2e6577866 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Thu, 18 Jun 2026 11:33:10 +0530 Subject: [PATCH 33/39] Implement Review fixes --- .../Trenser.VehicleServiceSystem/datastores/DataStore.h | 1 + .../services/ServiceManagementService.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index 7093599..03eb0eb 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -222,6 +222,7 @@ void DataStore::refreshCache(util::Map>& cac TrackedRecord& oldRecord = oldCache.getValueAt(oldIndex); if (oldRecord.state == RecordState::MODIFIED) { + delete refreshedRecord.data; cache.insert(id, oldRecord); continue; } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 172cda4..1a7693f 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -594,6 +594,10 @@ 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!"); @@ -607,10 +611,6 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const throw std::runtime_error("Invalid service booking id."); } auto& currentTrackedServiceBooking = currentTrackedServiceBookings.getValueAt(currentTrackedServiceBookingIndex); - if (currentBooking == nullptr) - { - throw std::runtime_error("Service Booking not available"); - } auto& currentServices = currentBooking->getServices(); if (currentServices.find(serviceID) == -1) { From 931913fa30e4ce8edebe4bb6c3b630148e664591 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Thu, 18 Jun 2026 19:16:50 +0530 Subject: [PATCH 34/39] Fix: remove inventory quantity from invoice display - Removed Quantity column from invoice item listing - Removed inventory stock quantity values from invoice output - Display only item name and price in generated invoices Fixes #2115 --- .../Trenser.VehicleServiceSystem/views/MenuHelper.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 33cd5bd..57180b0 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -597,7 +597,6 @@ inline void displayInvoices(util::Map currentUserIn std::cout << "\nItems Used:\n"; std::cout << std::left << std::setw(20) << "ItemName" - << std::setw(10) << "Quantity" << std::setw(10) << "Price" << std::endl; std::cout << std::string(40, '-') << std::endl; @@ -606,7 +605,6 @@ inline void displayInvoices(util::Map currentUserIn InventoryItem* currentItem = inventoryItemsInInvoice.getValueAt(iterator); std::cout << std::left << std::setw(20) << currentItem->getPartName() - << std::setw(10) << currentItem->getQuantity() << std::setw(10) << currentItem->getPrice() << std::endl; } From 2eaa719aca4415ad51271189ef6ab94800b5fa75 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Thu, 18 Jun 2026 19:21:59 +0530 Subject: [PATCH 35/39] Fix Multiple Notifications Contain Formatting Issues, Vague Messaging, and Duplicate Entries Changes: - Updated ServiceManagementService::createJobCard() to prevent duplicate "Technician assigned" notifications when multiple job cards exist for the same booking. - Refined job card creation notification to include Job Card ID, Service ID, and Booking ID for clearer technician communication. - Corrected service booking cancellation notification formatting by standardizing capitalization and improving message clarity. - Improved service booking completion notification to explicitly mention the booking ID and confirm invoice generation. - Ensured consistent notification titles and messages across the workflow for better user understanding. Fixes #2114 --- .../services/ServiceManagementService.cpp | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 5e0b30d..210de1e 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -629,6 +629,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const DataStoreLockGuard lock(m_dataStore); UserManagementService m_userManagementService; ServiceBooking* currentBooking = getServiceBooking(bookingID); + std::string title, message; if (currentBooking == nullptr) { throw std::runtime_error("Service Booking not available"); @@ -684,18 +685,31 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const trackedCurrentInventoryItem.state = RecordState::MODIFIED; } } - currentBooking->setAssignedTechnician(selectedTechnician); - currentBooking->setAssignedTechnicianId(selectedTechnician->getId()); + auto currentAssignedTechnician = currentBooking->getAssignedTechnician(); + auto currentAssignedTechnicianId = currentBooking->getAssignedTechnicianId(); + currentBooking->getAssignedTechnicianId(); + if (!currentAssignedTechnician && currentAssignedTechnicianId.empty()) + { + currentBooking->setAssignedTechnician(selectedTechnician); + currentBooking->setAssignedTechnicianId(selectedTechnician->getId()); + title = "Technician assigned"; + message = "A technician has been assigned to your Service Booking with ID " + bookingID; + sendNotification(currentBooking->getCustomer(), title, message); + } + if (currentBooking->getStatus() == util::ServiceJobStatus::PENDING) { 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()); if (jobCard) { + title = "Job Card Assigned"; + message = "A new Job Card (ID: " + jobCard->getId() + + ") has been created for Service " + serviceID + + " in Booking " + bookingID + + ". You have been assigned to this job."; currentTrackedJobCards.insert(jobCard->getId(), util::createNewRecord(jobCard)); sendNotification(selectedTechnician, title, message); } @@ -703,9 +717,6 @@ 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); m_dataStore.saveJobCards(); m_dataStore.saveServiceBookings(); m_dataStore.saveInventoryItems(); @@ -852,8 +863,8 @@ void ServiceManagementService::removeServiceBooking(const std::string& 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."; + const std::string title = "Service Booking Cancelled"; + const std::string message = "Service Booking (ID: " + bookingID + ") has been successfully cancelled"; currentServiceBooking->setStatus(util::ServiceJobStatus::CANCELLED); currentTrackedServiceBooking.state = RecordState::MODIFIED; serviceBookingRemoved = true; @@ -1025,8 +1036,8 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID) currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED); trackedServiceBookings.getValueAt(trackedServiceBookings.find(bookingId)).state = RecordState::MODIFIED; paymentManagementService.generateInvoice(currentJob->getBooking()); - std::string title = "Service Booking completed. Invoice Generated."; - std::string message = "Services completed for the booking and invoice generated."; + std::string title = "Service Booking Completed"; + std::string message = "Service Booking (ID: " + bookingId + ") has been completed successfully. An invoice has been generated."; sendNotification(currentJob->getBooking()->getCustomer(), title, message); } } From bdb8431773a3593650736a0670508606b5ce9e49 Mon Sep 17 00:00:00 2001 From: Avinash Rajesh Date: Thu, 18 Jun 2026 19:26:45 +0530 Subject: [PATCH 36/39] Customer Allowed to Create Service Bookings While Having Unpaid Invoices - Added verifyAllPaymentsCompleted() helper in MenuHelper.h to check if the authenticated customer has completed all invoices. - Integrated payment verification into CustomerMenu::selectService() to block new service bookings when pending payments exist. - Integrated payment verification into CustomerMenu::selectComboPackage() to block new combo package bookings when invoices are incomplete. - Implemented iteration over customer invoices to ensure only users with all payments marked as COMPLETED can proceed with new bookings. - Added user-facing messages to inform customers when bookings are denied due to outstanding payments. Fixes #2116 --- .../views/CustomerMenu.cpp | 14 +++++++ .../views/MenuHelper.h | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp index 09e145c..7b281d6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp @@ -220,6 +220,13 @@ void CustomerMenu::selectService() util::pressEnter(); return; } + if (!verifyAllPaymentsCompleted(m_controller)) + { + std::cout << "Your booking cannot be processed because you have pending payments for previous services. Please complete all outstanding invoices before booking a new service." + << std::endl; + util::pressEnter(); + return; + } util::Vector selectedServices; const Service* selectedService = selectServiceFromServices(services); if (selectedService == nullptr) @@ -262,6 +269,13 @@ void CustomerMenu::selectComboPackage() util::pressEnter(); return; } + if (!verifyAllPaymentsCompleted(m_controller)) + { + std::cout << "Your booking cannot be processed because you have pending payments for previous services. Please complete all outstanding invoices before booking a new combo package." + << std::endl; + util::pressEnter(); + return; + } const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(activeComboPackages); if (selectedComboPackage == nullptr) { diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 33cd5bd..6f6a1df 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -1467,3 +1467,45 @@ inline void displayNewNotification(util::Vector notificatio MB_ICONINFORMATION); } } + +/* +Function: verifyAllPaymentsCompleted +Description: Checks whether the authenticated customer has completed + all payments for their invoices. Iterates through all + invoices belonging to the customer and verifies that + each invoice has a payment status of COMPLETED. +Parameters: Controller& m_controller - + reference to the Controller object used to access + authenticated user and invoice data +Return type: bool + true if all invoices for the authenticated customer + are completed, false if any invoice is pending or not completed +Throws: std::runtime_error if no authenticated user is found +*/ +inline bool verifyAllPaymentsCompleted(Controller& m_controller) +{ + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + if (!authenticatedUser) + { + throw std::runtime_error("No authenticated user found."); + } + std::string authenticatedUserId = authenticatedUser->getId(); + util::Map listOfInvoices = m_controller.getAllInvoices(); + for (int invoiceIndex = 0; invoiceIndex < listOfInvoices.getSize(); ++invoiceIndex) + { + const Invoice* invoice = listOfInvoices.getValueAt(invoiceIndex); + if (!invoice) + { + continue; + } + std::string customerId = invoice->getBooking()->getCustomerId(); + if (customerId == authenticatedUserId) + { + if (invoice->getStatus() != util::PaymentStatus::COMPLETED) + { + return false; + } + } + } + return true; +} From 4fa143946e4ce92194ca41611dc58de8bb447b53 Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Thu, 18 Jun 2026 19:31:18 +0530 Subject: [PATCH 37/39] Fix combo package null handling and cleanup initialization - Added null checks in combo package display/selection flows - Replaced unnecessary copies with references - Zero-initialized SerializedObserver - Initialized event handle array --- .../datastores/DataStore.cpp | 12 +++--- .../views/Menu.cpp | 2 +- .../views/MenuHelper.h | 42 +++++++++++-------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 2d0d153..06d4dda 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -358,7 +358,7 @@ util::Map>& DataStore::getServiceBook { throw std::runtime_error("Invalid service index."); } - auto currentService = services.getValueAt(serviceIndex); + auto& currentService = services.getValueAt(serviceIndex); servicesInBooking[currentServiceId] = currentService.data; } serviceBooking->setServices(servicesInBooking); @@ -369,7 +369,7 @@ util::Map>& DataStore::getServiceBook { throw std::runtime_error("Invalid user index."); } - auto customer = users.getValueAt(userIndex); + auto& customer = users.getValueAt(userIndex); serviceBooking->setCustomer(customer.data); } if (!serviceBooking->getAssignedTechnicianId().empty()) @@ -379,7 +379,7 @@ util::Map>& DataStore::getServiceBook { throw std::runtime_error("Invalid technician index."); } - auto technician = users.getValueAt(technicianIndex); + auto& technician = users.getValueAt(technicianIndex); serviceBooking->setAssignedTechnician(technician.data); } } @@ -423,7 +423,7 @@ util::Map>& DataStore::getJobCards() { throw std::runtime_error("Invalid service ID: " + serviceId); } - auto trackedService = services.getValueAt(serviceIndex); + auto& trackedService = services.getValueAt(serviceIndex); jobCard->setService(trackedService.data); const std::string& technicianId = jobCard->getTechnicianId(); if (!technicianId.empty()) @@ -433,7 +433,7 @@ util::Map>& DataStore::getJobCards() { throw std::runtime_error("Invalid technician ID: " + technicianId); } - auto trackedTechnician = users.getValueAt(technicianIndex); + auto& trackedTechnician = users.getValueAt(technicianIndex); jobCard->setTechnician(trackedTechnician.data); } } @@ -691,7 +691,7 @@ void DataStore::saveObservers(MappingInfo& mapping, util::Map(index)); strcpy_s(serializedObserver.id, sizeof(serializedObserver.id), user->getId().c_str()); SerializedObserver* destination = static_cast(SharedMemory::getRecordAddress(mapping, index)); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp index e5881ff..05f2496 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp @@ -67,7 +67,7 @@ Return type: void */ void Menu::eventListenerLoop() { - HANDLE handles[3]; + HANDLE handles[3] = { NULL, NULL, NULL }; handles[0] = m_accountDisabledEvent; handles[1] = m_notificationAvailableEvent; handles[2] = m_shutdownEvent; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 57180b0..a974b8f 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -588,7 +588,7 @@ inline void displayInvoices(util::Map currentUserIn << util::getPaymentStatusString(selectedInvoice->getStatus()) << std::endl; std::cout << std::left << std::setw(20) << "Payment Mode:" << util::getPaymentModeString(selectedInvoice->getPaymentMethod()) << std::endl; - auto inventoryItemsInInvoice = selectedInvoice->getParts(); + auto& inventoryItemsInInvoice = selectedInvoice->getParts(); if (inventoryItemsInInvoice.isEmpty()) { std::cout << "No inventory items used.\n\n"; @@ -1144,15 +1144,18 @@ inline void displayAllComboPackages(util::Map for (int index = 0; index < comboPackages.getSize(); index++) { const ComboPackage* currentComboPackage = comboPackages.getValueAt(index); - if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE) + if (currentComboPackage) { - continue; + if (currentComboPackage->getState() != util::State::ACTIVE) + { + continue; + } + std::cout << std::left + << std::setw(15) << currentComboPackage->getId() + << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30) + << std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage) + << std::endl; } - std::cout << std::left - << std::setw(15) << currentComboPackage->getId() - << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30) - << std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage) - << std::endl; } } @@ -1178,18 +1181,21 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::MapgetState() != util::State::ACTIVE) + if (currentComboPackage) { - continue; + if (currentComboPackage->getState() != util::State::ACTIVE) + { + continue; + } + activeComboPackages.insert(currentIndex, currentComboPackage); + std::cout << std::left + << std::setw(10) << currentIndex + << std::setw(15) << currentComboPackage->getId() + << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30) + << std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage) + << std::endl; + currentIndex++; } - activeComboPackages.insert(currentIndex, currentComboPackage); - std::cout << std::left - << std::setw(10) << currentIndex - << std::setw(15) << currentComboPackage->getId() - << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30) - << std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage) - << std::endl; - currentIndex++; } if (activeComboPackages.getSize() == 0) { From 684d6d38602d768204fa67ca16590a3a8a9bb6db Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Thu, 18 Jun 2026 19:43:41 +0530 Subject: [PATCH 38/39] Implement review fixes --- .../services/ServiceManagementService.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 210de1e..e904d25 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -685,9 +685,8 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const trackedCurrentInventoryItem.state = RecordState::MODIFIED; } } - auto currentAssignedTechnician = currentBooking->getAssignedTechnician(); - auto currentAssignedTechnicianId = currentBooking->getAssignedTechnicianId(); - currentBooking->getAssignedTechnicianId(); + const User* currentAssignedTechnician = currentBooking->getAssignedTechnician(); + const std::string& currentAssignedTechnicianId = currentBooking->getAssignedTechnicianId(); if (!currentAssignedTechnician && currentAssignedTechnicianId.empty()) { currentBooking->setAssignedTechnician(selectedTechnician); From 956ef58c7963178e139cb773b9f0a764a4da3aae Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Thu, 18 Jun 2026 19:54:12 +0530 Subject: [PATCH 39/39] Implement review fixes --- .../Trenser.VehicleServiceSystem/views/MenuHelper.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h index 6f6a1df..dafd9ad 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/MenuHelper.h @@ -1489,7 +1489,7 @@ inline bool verifyAllPaymentsCompleted(Controller& m_controller) { throw std::runtime_error("No authenticated user found."); } - std::string authenticatedUserId = authenticatedUser->getId(); + const std::string& authenticatedUserId = authenticatedUser->getId(); util::Map listOfInvoices = m_controller.getAllInvoices(); for (int invoiceIndex = 0; invoiceIndex < listOfInvoices.getSize(); ++invoiceIndex) { @@ -1498,7 +1498,7 @@ inline bool verifyAllPaymentsCompleted(Controller& m_controller) { continue; } - std::string customerId = invoice->getBooking()->getCustomerId(); + const std::string& customerId = invoice->getBooking()->getCustomerId(); if (customerId == authenticatedUserId) { if (invoice->getStatus() != util::PaymentStatus::COMPLETED)