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 8627061..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;%(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;%(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,8 +126,10 @@ + + @@ -139,7 +141,6 @@ - @@ -148,14 +149,23 @@ + + + + + + + + + @@ -174,7 +184,6 @@ - @@ -185,6 +194,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters index 73b9291..ac65e97 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj.filters @@ -64,6 +64,18 @@ {8057b93d-51a9-42df-b06e-01ce395f6308} + + {d9da9793-fe6f-4914-bee3-99d5934da228} + + + {0769afb6-f57d-4ae3-a1cf-ceca6e606af0} + + + {85029bdb-6941-41dc-a3a7-9e5841671d8c} + + + {1050aca7-6f2c-4ccb-a446-db9c898c3599} + @@ -108,9 +120,6 @@ Source Files\DataStores - - Source Files\Services - Source Files\Core\Patterns @@ -141,6 +150,15 @@ Source Files\Models + + Source Files\Core\SharedMemory + + + Source Files\Core\Events + + + Source Files\Views + @@ -236,9 +254,6 @@ Header Files\Utilities - - Header Files\Utilities - Header Files\Utilities @@ -251,5 +266,32 @@ Header Files\Views + + 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 e3a85b0..28441fb 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" @@ -233,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. @@ -264,6 +276,7 @@ util::Map Controller::getServiceBookings() return readOnlyServiceBookings; } + /* Function: getServiceBookingsByUser Description: Retrieves all service bookings for a specific user. @@ -587,63 +600,50 @@ 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(); +} + +/* +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 35241a0..2120fe5 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" @@ -57,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); @@ -70,7 +72,7 @@ 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(); + 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/core/patterns/Observer.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h index 7fd7262..98e6f97 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/patterns/Observer.h @@ -7,11 +7,8 @@ Date: 19-May-2026 #pragma once -class Notification; - 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/core/sharedmemory/FileHeader.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/FileHeader.h new file mode 100644 index 0000000..929dbf4 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/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/core/sharedmemory/MappingInfo.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/MappingInfo.h new file mode 100644 index 0000000..ebd5123 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/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/core/sharedmemory/RecordState.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/RecordState.h new file mode 100644 index 0000000..95402fd --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/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/core/sharedmemory/SerializedRecords.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SerializedRecords.h new file mode 100644 index 0000000..2d18842 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/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/core/sharedmemory/SharedMemory.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.cpp new file mode 100644 index 0000000..0c5c7b1 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.cpp @@ -0,0 +1,386 @@ +/* +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 "Windows.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 * config::file::GROWTH_FACTOR); +} + +/* +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/core/sharedmemory/SharedMemory.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/SharedMemory.h new file mode 100644 index 0000000..a288ab3 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/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/core/sharedmemory/TrackedRecord.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/sharedmemory/TrackedRecord.h new file mode 100644 index 0000000..5ff9ac9 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/core/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/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index 3419f44..06d4dda 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -9,6 +9,197 @@ Date: 19-May-2026 */ #include "DataStore.h" +#include "Config.h" +#include "SerializedRecords.h" +#include "FileHelper.h" +#include "ServiceBooking.h" +#include "JobCard.h" +#include "Invoice.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_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_serviceManagementObservers); + SharedMemory::closeMapping(m_paymentManagementObservers); + SharedMemory::closeMapping(m_inventoryManagementObservers); + if (m_globalMutex != NULL) + { + CloseHandle(m_globalMutex); + m_globalMutex = NULL; + } +} /* Function: getInstance @@ -26,104 +217,567 @@ 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; + auto users = loadRecords(m_users); + refreshCache(m_userCache, 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() +{ + auto notifications = loadRecords(m_notifications); + refreshCache(m_notificationCache, notifications); + 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; + 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; } /* 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; + 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; } /* 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; + auto inventoryItems = loadRecords(m_inventoryItems); + refreshCache(m_inventoryItemCache, 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() +{ + 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; +} + +/* +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() +{ + 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 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; + 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; } /* -Function: getPayments -Description: Retrieves the internal map of payments. +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. 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; + return getObservers(m_serviceManagementObservers); +} + +/* +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 getObservers(m_paymentManagementObservers); +} + +/* +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 getObservers(m_inventoryManagementObservers); +} + +/* +Function: saveUsers +Description: Persists all user records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveUsers() +{ + saveRecords(m_users, m_userCache); +} + +/* +Function: saveNotifications +Description: Persists all notification records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveNotifications() +{ + saveRecords(m_notifications, m_notificationCache); +} + +/* +Function: saveServices +Description: Persists all service records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveServices() +{ + saveRecords(m_services, m_serviceCache); +} + +/* +Function: saveComboPackages +Description: Persists all combo package records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveComboPackages() +{ + saveRecords(m_comboPackages, m_comboPackageCache); +} + +/* +Function: saveInventoryItems +Description: Persists all inventory item records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveInventoryItems() +{ + saveRecords(m_inventoryItems, m_inventoryItemCache); +} + +/* +Function: saveServiceBookings +Description: Persists all service booking records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveServiceBookings() +{ + saveRecords(m_serviceBookings, m_serviceBookingCache); +} + +/* +Function: saveJobCards +Description: Persists all job card records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveJobCards() +{ + saveRecords(m_jobCards, m_jobCardCache); +} + +/* +Function: saveInvoices +Description: Persists all invoice records to the datastore. +Parameters: + - None +Returns: + - None +*/ +void DataStore::saveInvoices() +{ + saveRecords(m_invoices, m_invoiceCache); +} + +/* +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 +Returns: + - None +*/ +void DataStore::saveServiceManagementObservers(util::Map& observers) +{ + saveObservers(m_serviceManagementObservers, 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) +{ + saveObservers(m_paymentManagementObservers, 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) +{ + saveObservers(m_inventoryManagementObservers, observers); +} + +/* +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; } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h index cde9b4e..03eb0eb 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.h @@ -6,42 +6,248 @@ Date: 19-May-2026 */ #pragma once +#include #include #include "Map.h" - -class User; -class Service; -class ComboPackage; -class ServiceBooking; -class JobCard; -class InventoryItem; -class Invoice; -class Payment; +#include "MappingInfo.h" +#include "TrackedRecord.h" +#include "SerializedRecords.h" +#include "SharedMemory.h" +#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 { 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_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); + util::Map getObservers(MappingInfo& mapping); + void saveObservers(MappingInfo& mapping, util::Map& observers); +}; + +/* +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); + 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; + 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); + const TrackedRecord& localTrackedRecord = oldCache.getValueAt(index); + if (cache.find(id) == -1) + { + if (localTrackedRecord.state == RecordState::NEW_RECORD) + { + cache.insert(id, localTrackedRecord); + } + else + { + delete localTrackedRecord.data; + } + } + } +} \ No newline at end of file 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/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..671e346 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 { @@ -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/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 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 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/Notification.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp index 0bae917..83532ec 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/Notification.cpp @@ -1,12 +1,13 @@ /* 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 */ #include +#include "SerializedRecords.h" #include "Notification.h" #include "StringHelper.h" #include "Factory.h" @@ -22,8 +23,8 @@ Returns: - A new Notification object. */ Notification::Notification() - : m_id("NOT" + std::to_string(++m_uid)), - m_recipient(nullptr) {} + : m_id("NOT" + std::to_string(++m_uid)), + m_state(util::State::ACTIVE) {} /* Function: Notification @@ -37,13 +38,14 @@ 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_createdAt(createdAt) {} + m_state(util::State::ACTIVE), + m_createdAt(createdAt) { +} /* Function: Notification (parameterized constructor with ID) @@ -58,13 +60,13 @@ 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 +81,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; } @@ -95,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. @@ -139,6 +130,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. @@ -165,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. @@ -217,71 +206,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..a102cc6 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 { @@ -18,27 +20,26 @@ 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& id, const std::string& recipientUserId, 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); - 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&); }; 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 diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/User.cpp index 0b4e86e..5794259 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) @@ -86,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. @@ -169,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. @@ -280,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. @@ -324,68 +283,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..1a0f93f 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 { @@ -25,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); @@ -48,10 +47,8 @@ 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); - std::string serialize() const; - static User* deserialize(const std::string&); - static std::string getHeaders(); + SerializedUser serialize() const; + static User* deserialize(const SerializedUser& serializedUser); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp index dc3a94c..dd80cd9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.cpp @@ -10,8 +10,32 @@ Date:19-May-2026 #include #include "AuthenticationManagementService.h" #include "User.h" +#include "Utility.h" +#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 @@ -24,16 +48,35 @@ 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()) { m_authenticatedUser = user; + m_isAuthorized = true; + m_eventManager.initialize( + user->getId(), + []() + { + if (m_accountDisabledEvent) + { + AuthenticationManagementService::m_isAuthorized = false; + SetEvent(m_accountDisabledEvent); + } + }, + []() + { + if (m_notificationsAvailableEvent) + { + SetEvent(m_notificationsAvailableEvent); + } + }); return true; } return false; @@ -62,7 +105,11 @@ Return type: void */ void AuthenticationManagementService::logout() { + m_eventManager.shutdown(); m_authenticatedUser = nullptr; + m_isAuthorized = false; + m_accountDisabledEvent = NULL; + m_notificationsAvailableEvent = NULL; } /* @@ -74,9 +121,33 @@ 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) { 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"); + } m_authenticatedUser->setPassword(newPassword); + 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 notificationAvailableEvent) +{ + m_accountDisabledEvent = accountDisabledEvent; + m_notificationsAvailableEvent = notificationAvailableEvent; } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/AuthenticationManagementService.h index 47266a1..e927187 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,11 +19,17 @@ 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); 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 d08d957..6152fd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/InventoryManagementService.cpp @@ -12,14 +12,15 @@ Date: 22-May-2026 #include "Config.h" #include "Enums.h" #include "Factory.h" -#include "FileManager.h" #include "InventoryItem.h" #include "InventoryManagementService.h" +#include "AuthenticationManagementService.h" #include "Timestamp.h" #include "User.h" #include "Utility.h" #include "Vector.h" - +#include "DataStoreLockGuard.h" +#include "EventManager.h" util::Map InventoryManagementService::m_observers{}; @@ -58,18 +59,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 +84,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 +92,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 +103,12 @@ 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); - m_dataStore.getInventoryItems().insert(newItem->getId(), newItem); + trackedInventoryItemMap.insert(newItem->getId(), util::createNewRecord(newItem)); + m_dataStore.saveInventoryItems(); } /* @@ -203,16 +120,23 @@ 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); - } - } + AuthenticationManagementService::ensureAuthorization(); + 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 +147,11 @@ Return type: util::Map */ util::Map InventoryManagementService::getInventoryItems() { - return m_dataStore.getInventoryItems(); + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); + auto inventoryMap = util::getObjects(trackedInventoryItemMap); + return inventoryMap; } /* @@ -234,15 +162,22 @@ 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); - } - } + AuthenticationManagementService::ensureAuthorization(); + 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 +188,20 @@ Return type: InventoryItem* */ InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID) { - int index = m_dataStore.getInventoryItems().find(inventoryItemID); - if (index != -1) + AuthenticationManagementService::ensureAuthorization(); + 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; } /* @@ -271,6 +214,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 +225,7 @@ void InventoryManagementService::attach(User* user) m_observers[userID] = user; } } + m_dataStore.saveInventoryManagementObservers(m_observers); } /* @@ -291,6 +238,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 +249,7 @@ void InventoryManagementService::detach(User* user) m_observers.remove(userID); } } + m_dataStore.saveInventoryManagementObservers(m_observers); } /* @@ -315,27 +266,28 @@ 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; + } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + Notification* notification = Factory::getObject( + user->getId(), + title, + message, + util::Timestamp()); + if (!notification) + { + throw std::runtime_error("Failed to create notification"); + } + trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); + m_dataStore.saveNotifications(); + EventManager::sendNotificationAvailableEvent(user->getId()); } 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.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/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 1fc1ff8..b09b9bf 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -11,16 +11,19 @@ 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" #include "PaymentManagementService.h" +#include "AuthenticationManagementService.h" +#include "DataStoreLockGuard.h" #include "Service.h" #include "ServiceBooking.h" #include "Timestamp.h" #include "User.h" #include "Utility.h" +#include "DataStoreLockGuard.h" +#include "EventManager.h" util::Map PaymentManagementService::m_observers{}; @@ -34,6 +37,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 +48,7 @@ void PaymentManagementService::attach(User* user) m_observers[userID] = user; } } + m_dataStore.savePaymentManagementObservers(m_observers); } /* @@ -54,6 +61,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 +72,7 @@ void PaymentManagementService::detach(User* user) m_observers.remove(userID); } } + m_dataStore.savePaymentManagementObservers(m_observers); } /* @@ -78,28 +89,29 @@ 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; + } + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + Notification* notification = Factory::getObject( + user->getId(), + title, + message, + util::Timestamp()); + if (!notification) + { + throw std::runtime_error("Failed to create notification"); + } + trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); + m_dataStore.saveNotifications(); + EventManager::sendNotificationAvailableEvent(user->getId()); } /* @@ -113,11 +125,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(); @@ -140,123 +153,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. @@ -290,6 +186,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 +196,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 +219,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 +234,13 @@ Returns: */ util::Map PaymentManagementService::getInvoices(const std::string& customerID) { - util::Map& currentInvoices = m_dataStore.getInvoices(); + AuthenticationManagementService::ensureAuthorization(); + 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 +263,14 @@ Throws: */ void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode) { - auto& currentInvoices = m_dataStore.getInvoices(); - int invoiceIndex = currentInvoices.find(invoiceID); + AuthenticationManagementService::ensureAuthorization(); + 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,25 +281,31 @@ 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(); } /* -Function: getAllInvoice +Function: getAllInvoices 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() { - return m_dataStore.getInvoices(); + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); + util::Map invoices; + invoices = util::getObjects(m_dataStore.getInvoices()); + return invoices; } /* @@ -412,20 +321,25 @@ Throws: */ void PaymentManagementService::confirmPayment(const std::string& invoiceID) { - auto& currentInvoices = m_dataStore.getInvoices(); - int invoiceIndex = currentInvoices.find(invoiceID); + AuthenticationManagementService::ensureAuthorization(); + 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 diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h index 8961a2d..ca5cacf 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.h @@ -22,20 +22,15 @@ 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); 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; 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 1a9f753..e904d25 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" @@ -25,7 +24,34 @@ Date:19-May-2026 #include "Timestamp.h" #include "User.h" #include "UserManagementService.h" +#include "DataStoreLockGuard.h" #include "Utility.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 @@ -40,24 +66,26 @@ 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) { 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,10 +93,12 @@ 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); + m_dataStore.saveServiceBookings(); + notifyAllAdmins("New Service Order Available", "A new service order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this); } /* @@ -84,30 +114,34 @@ 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) { 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); + m_dataStore.saveServiceBookings(); + notifyAllAdmins("New Combo Package Order Available", "A new combo package order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this); } util::Map ServiceManagementService::m_observers{}; @@ -122,6 +156,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 +167,7 @@ void ServiceManagementService::attach(User* user) m_observers[userID] = user; } } + m_dataStore.saveServiceManagementObservers(m_observers); } /* @@ -142,6 +180,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 +191,7 @@ void ServiceManagementService::detach(User* user) m_observers.remove(userID); } } + m_dataStore.saveServiceManagementObservers(m_observers); } /* @@ -166,345 +208,29 @@ 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; -} - -/* -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++) + if (!user) { - 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); + return; } -} - -/* -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++) + DataStoreLockGuard lock(m_dataStore); + m_observers = m_dataStore.getServiceManagementObservers(); + if (m_observers.find(user->getId()) == -1) { - 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; + return; } -} - -/* -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++) + auto& trackedNotificationsMap = m_dataStore.getNotifications(); + Notification* notification = Factory::getObject( + user->getId(), + title, + message, + util::Timestamp()); + if (!notification) { - 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; + throw std::runtime_error("Failed to create notification"); } -} - -/* -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); + trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification)); + m_dataStore.saveNotifications(); + EventManager::sendNotificationAvailableEvent(user->getId()); } /* @@ -514,7 +240,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 +259,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 +287,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 +341,8 @@ static void processBookingCancellation(ServiceBooking* booking, } booking->setAssignedTechnician(nullptr); booking->setAssignedTechnicianId(""); - restoreInventory(booking); + trackedBooking.state = RecordState::MODIFIED; + restoreInventory(booking, trackedInventoryItems); } /* @@ -615,22 +355,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 +388,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 +405,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 +444,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 +463,8 @@ 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()) { throw std::invalid_argument("The Combo Package Name cannot be empty.\n"); @@ -721,19 +477,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 +513,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 +533,11 @@ Return type: util::Map */ util::Map ServiceManagementService::getComboPackages() { - return m_dataStore.getComboPackages(); + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); + util::Map comboPackages; + comboPackages = util::getObjects(m_dataStore.getComboPackages()); + return comboPackages; } /* @@ -787,14 +548,18 @@ Return type: void */ void ServiceManagementService::removeComboPackage(const std::string& comboPackageID) { + AuthenticationManagementService::ensureAuthorization(); + 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 +568,7 @@ void ServiceManagementService::removeComboPackage(const std::string& comboPackag { throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found."); } + m_dataStore.saveComboPackages(); } /* @@ -815,7 +581,11 @@ Returns: */ util::Map ServiceManagementService::getServiceBookings() { - return m_dataStore.getServiceBookings(); + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); + util::Map serviceBookings; + serviceBookings = util::getObjects(m_dataStore.getServiceBookings()); + return serviceBookings; } /* @@ -828,6 +598,7 @@ Returns: */ ServiceBooking* ServiceManagementService::getServiceBooking(const std::string& serviceID) { + AuthenticationManagementService::ensureAuthorization(); auto currentServiceBookings = getServiceBookings(); for (int iterator = 0; iterator < currentServiceBookings.getSize(); iterator++) { @@ -854,13 +625,28 @@ 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); - auto& currentJobCards = m_dataStore.getJobCards(); - if (currentBooking == nullptr) - { - throw std::runtime_error("Service Booking not available"); - } + std::string title, message; + if (currentBooking == nullptr) + { + throw std::runtime_error("Service Booking not available"); + } + if (currentBooking->getStatus() == util::ServiceJobStatus::CANCELLED) + { + throw std::runtime_error("Cannot create job card. Service Booking was cancelled!"); + } + auto& currentTrackedJobCards = m_dataStore.getJobCards(); + auto& currentTrackedInventoryItems = m_dataStore.getInventoryItems(); + auto& currentTrackedServiceBookings = m_dataStore.getServiceBookings(); + int currentTrackedServiceBookingIndex = currentTrackedServiceBookings.find(bookingID); + if (currentTrackedServiceBookingIndex == -1) + { + throw std::runtime_error("Invalid service booking id."); + } + auto& currentTrackedServiceBooking = currentTrackedServiceBookings.getValueAt(currentTrackedServiceBookingIndex); auto& currentServices = currentBooking->getServices(); if (currentServices.find(serviceID) == -1) { @@ -885,33 +671,54 @@ 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); - currentBooking->setAssignedTechnicianId(selectedTechnician->getId()); + const User* currentAssignedTechnician = currentBooking->getAssignedTechnician(); + const std::string& currentAssignedTechnicianId = 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); } - std::string title = "Job card created"; - std::string message = "Job card created for the service and you are assigned for that."; + currentTrackedServiceBooking.state = RecordState::MODIFIED; JobCard* jobCard = Factory::getObject(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp()); if (jobCard) { - currentJobCards.insert(jobCard->getId(), 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); } else { 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(); } /* @@ -929,15 +736,18 @@ Throws: */ void ServiceManagementService::createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost) { + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); util::Map currentServiceInventoryItems; - auto inventoryItems = m_dataStore.getInventoryItems(); + auto& trackedInventoryItems = m_dataStore.getInventoryItems(); + auto& currentServices = m_dataStore.getServices(); 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 +765,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(); 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)); + m_dataStore.saveServices(); } /* @@ -973,7 +783,11 @@ Returns: */ util::Map ServiceManagementService::getServices() { - return m_dataStore.getServices(); + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); + util::Map services; + services = util::getObjects(m_dataStore.getServices()); + return services; } /* @@ -988,14 +802,20 @@ 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) + AuthenticationManagementService::ensureAuthorization(); + 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 +825,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 +836,63 @@ void ServiceManagementService::removeService(const std::string& serviceID) { throw std::runtime_error("Service not found."); } + m_dataStore.saveServices(); + 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 (ID: " + bookingID + ") has been 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(); } /* @@ -1027,6 +905,7 @@ Returns: */ util::Map ServiceManagementService::getServiceBookings(const std::string& customerID) { + AuthenticationManagementService::ensureAuthorization(); util::Map currentServiceBookings = getServiceBookings(); util::Map currentUserServiceBookings; if (currentServiceBookings.getSize() != 0) @@ -1053,11 +932,13 @@ Returns: */ util::Map ServiceManagementService::getJobCards(const std::string& technicianID) { - util::Map jobCards = m_dataStore.getJobCards(); + AuthenticationManagementService::ensureAuthorization(); + 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,53 +987,68 @@ Returns: */ void ServiceManagementService::updateJobStatus(const std::string& jobID) { - 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."); - } - if (currentAssignedJobs.find(jobID) != -1) - { - 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); - jobStatusUpdated = true; - } + AuthenticationManagementService::ensureAuthorization(); + DataStoreLockGuard lock(m_dataStore); + 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) + { + int jobIndex = trackedJobCards.find(jobID); + if (jobIndex == -1) + { + throw std::runtime_error("Unable to fetch current job."); + } + 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); + trackedCurrentJob.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."; + 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); } } - } - else - { - throw std::runtime_error("Failed to update job status. Job may already be completed."); - } - if (!jobStatusUpdated) - { - throw std::runtime_error("Failed to update job status. Job may already be completed."); - } + } + else + { + throw std::runtime_error("Failed to update job status. Job may already be completed."); + } + if (!jobStatusUpdated) + { + throw std::runtime_error("Failed to update job status. Job may already be completed."); + } + m_dataStore.saveJobCards(); + m_dataStore.saveServiceBookings(); } \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h index b2a4384..7504a29 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(); @@ -36,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); @@ -45,14 +45,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(); - void loadObservers(); - void saveObservers(); }; diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 8ba68d0..0c3991d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -11,15 +11,19 @@ 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" #include "ServiceManagementService.h" +#include "AuthenticationManagementService.h" #include "User.h" #include "UserManagementService.h" #include "Vector.h" #include "Validator.h" +#include "Utility.h" +#include "TrackedRecord.h" +#include "DataStoreLockGuard.h" +#include "EventManager.h" /* Function: ensureAdminExists @@ -31,12 +35,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 +78,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 +94,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 +115,25 @@ 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); + AuthenticationManagementService::ensureAuthorization(); + 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 +141,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,20 +163,26 @@ Throws: */ util::Vector UserManagementService::getUserNotifications(const std::string& userID) { - auto& usersMap = m_dataStore.getUsers(); - if (usersMap.find(userID) == -1) + AuthenticationManagementService::ensureAuthorization(); + 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(); - 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; } @@ -169,97 +194,40 @@ 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) + AuthenticationManagementService::ensureAuthorization(); + 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]; - auto& notifications = user->getNotifications(); - if (notifications.find(notificationID) == -1) + int notificationIndex = trackedNotificationsMap.find(notificationID); + if (notificationIndex == -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); + Notification* notification = trackedNotificationsMap.getValueAt(notificationIndex).data; + if (notification->getRecipientUserId() == userID) + { + notification->setState(util::State::INACTIVE); + trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED; + m_dataStore.saveNotifications(); + } } /* @@ -270,7 +238,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 +251,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; } @@ -297,38 +269,65 @@ Return type: void */ void UserManagementService::removeUser(const std::string& userID) { + AuthenticationManagementService::ensureAuthorization(); InventoryManagementService inventoryManagementService; PaymentManagementService paymentManagementService; ServiceManagementService serviceManagementService; - int index = m_dataStore.getUsers().find(userID); - if (index != -1) + std::string removedUserID; { - User* user = m_dataStore.getUsers().getValueAt(index); - 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); - } - user->setState(util::State::INACTIVE); - inventoryManagementService.detach(user); - paymentManagementService.detach(user); - serviceManagementService.detach(user); } } + if (!removedUserID.empty()) + { + EventManager::sendUserDisabledEvent(removedUserID); + } } -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 6b9f518..28aa446 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" @@ -30,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/utilities/Config.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h index b0bd19d..a2995b4 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Config.h @@ -28,16 +28,19 @@ 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; + 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"; + 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..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 75d8bbc..065a197 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 { @@ -27,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"); } } @@ -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..56dd765 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Utility.h @@ -54,49 +54,79 @@ 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)); - } - } + template + Map getObjects(const Map>& trackedRecords); - /* - 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) + 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) { - auto observerIDs = service->getObserverIDs(); - util::saveRecords(filePath, observerIDs); + 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/AdminMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp index 76a1901..62c64cd 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,25 @@ 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(); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string name; + if (authenticatedUser) + { + name = authenticatedUser->getName(); + } + displayNewNotification(notifications, name); +} + /* Function: logout Description: Logs out the currently authenticated admin user. @@ -307,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/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 3e56d99..7b281d6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/CustomerMenu.cpp @@ -30,13 +30,19 @@ Description: Displays the customer menu and handles user input until logout is s Parameter: None Return type: void */ - void CustomerMenu::showMenu() { + + startEventListener(); while (true) { try { + if (!m_isMenuActive) + { + logout(); + break; + } int choice; util::clear(); std::cout << "Customer Menu" @@ -45,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)) @@ -63,6 +70,7 @@ void CustomerMenu::showMenu() util::pressEnter(); } } + stopEventListener(); } /* @@ -73,6 +81,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: @@ -91,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: @@ -112,6 +128,25 @@ 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(); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string name; + if (authenticatedUser) + { + name = authenticatedUser->getName(); + } + displayNewNotification(notifications, name); +} + /* Function: logout Description: Logs out the currently authenticated customer user. @@ -185,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) @@ -227,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) { @@ -302,6 +351,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 d491720..9976300 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(); @@ -27,5 +28,6 @@ public: void completePayments(); void viewInvoices(); void viewNotifications(); + void cancelServiceBooking(); void configureNotifications(); -}; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp new file mode 100644 index 0000000..05f2496 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/Menu.cpp @@ -0,0 +1,150 @@ +/* +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] = { NULL, NULL, NULL }; + 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); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string messageTitle = "Account Disabled"; + if (authenticatedUser) + { + messageTitle += " - " + authenticatedUser->getName(); + } + MessageBoxA( + GetConsoleWindow(), + "Your account has been disabled.", + messageTitle.c_str(), + 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 83720c0..f8dea52 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 @@ -587,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"; @@ -596,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; @@ -605,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; } @@ -914,7 +913,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 +927,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"; @@ -1056,6 +1055,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 @@ -1130,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; } } @@ -1164,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) { @@ -1407,4 +1427,89 @@ inline std::string selectComboPackage(util::Map 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, const std::string& name) +{ + const Notification* notification = nullptr; + size_t numberOfNotifications = notifications.getSize(); + for (int index = 0; index < numberOfNotifications; index++) + { + if (!notification) + { + notification = notifications[index]; + } + else + { + if (util::extractNumber(notification->getId()) < util::extractNumber(notifications[index]->getId())) + { + notification = notifications[index]; + } + } + } + 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); + } +} + +/* +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."); + } + const 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; + } + const std::string& customerId = invoice->getBooking()->getCustomerId(); + if (customerId == authenticatedUserId) + { + if (invoice->getStatus() != util::PaymentStatus::COMPLETED) + { + return false; + } + } + } + return true; +} diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/TechnicianMenu.cpp index 8ff3e12..ee4576c 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,25 @@ 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(); + const User* authenticatedUser = m_controller.getAuthenticatedUser(); + std::string name; + if (authenticatedUser) + { + name = authenticatedUser->getName(); + } + displayNewNotification(notifications, name); +} + /* 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 a17ccb1..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(); @@ -22,4 +23,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..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) { @@ -106,7 +109,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 +170,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 +188,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