Compare commits

..

2 Commits

Author SHA1 Message Date
Avinash Rajesh 74dbbd9e82 Implement Service Refactoring
<UserStory> 1952: Service Refactoring </UserStory>
UserStory #1952
<Changes>

1. Enhanced DataStore::getUsers to load SerializedUser records, refresh cache,
   and attach notifications to recipient users with validation for recipient IDs.

2. Updated DataStore::saveUsers to persist SerializedUser records and save
   notifications alongside user data.

3. Refactored AuthenticationManagementService::login to use DataStoreLockGuard
   and tracked user map with SerializedUser-backed records.

4. Modified AuthenticationManagementService::changePassword to ensure thread-safe
   updates, mark user record as MODIFIED, and persist changes via DataStore::saveUsers.

5. Added dependencies for Utility.h and DataStoreLockGuard.h in
   AuthenticationManagementService.cpp to support safe record handling.

</Changes>

<Test>

N/A

</Test>

<Review>

Sreeja Reghukumar

</Review>
2026-06-12 14:38:28 +05:30
Avinash Rajesh 6dea303b92 Implement Model Refactoring
<UserStory> 1951: Model Refactoring</UserStory>

UserStory #1951

<Changes>

1. Replaced CSV-based serialization and deserialization in ComboPackage,
   JobCard, Service, and ServiceBooking models with fixed-size SerializedRecord
   structures for shared memory storage.

2. Implemented serialize() methods to convert objects into SerializedComboPackage,
   SerializedJobCard, SerializedService, and SerializedServiceBooking records.

3. Implemented deserialize() methods to reconstruct objects directly from
   SerializedRecord types instead of parsing CSV strings.

4. Updated model class interfaces to use SerializedRecord types, removing
   legacy CSV serialization APIs and header generation functions.

5. Added SerializedRecords.h dependencies and forward declarations for
   Serialized structures across affected models.

</Changes>

<Test>

N/A

</Test>

<Review>

Sreeja Reghukumar

</Review>
2026-06-12 11:09:35 +05:30
9 changed files with 176 additions and 123 deletions
@@ -278,5 +278,8 @@
<ClInclude Include="datastores\sharedmemory\SharedMemory.h">
<Filter>Header Files\DataStores\SharedMemory</Filter>
</ClInclude>
<ClInclude Include="datastores\DataStoreLockGuard.h">
<Filter>Header Files\DataStores</Filter>
</ClInclude>
</ItemGroup>
</Project>
@@ -588,37 +588,63 @@ void Controller::configureNotifications(bool paymentNotifications, bool serviceN
}
/*
Function: initialize
Description: Initializes the system and run system checks to ensure critical configurations, such as verifying admin existence.
Parameters:
- None
Returns:
- bool
*/
bool Controller::initialize()
{
auto& dataStore = DataStore::getInstance();
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
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.
Parameters:
- None
Returns:
- void
*/
void Controller::shutdown()
void Controller::loadSystemData()
{
auto& dataStore = DataStore::getInstance();
dataStore.shutdown();
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();
}
/*
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()
{
m_userManagementService.ensureAdminExists();
m_inventoryManagementService.sendLowStockAlerts();
m_paymentManagementService.sendPaymentReminders();
}
@@ -70,6 +70,7 @@ public:
util::Vector<const Notification*> getNotifications();
void deleteNotification(const std::string& notificationID);
void configureNotifications(bool paymentNotifications, bool serviceNotifications);
bool initialize();
void shutdown();
void loadSystemData();
void saveSystemData();
void runSystemChecks();
};
@@ -257,8 +257,6 @@ Returns:
*/
util::Map<std::string, TrackedRecord<Notification>>& DataStore::getNotifications()
{
auto notifications = loadRecords<Notification, SerializedNotification>(m_notifications);
refreshCache(m_notificationCache, notifications);
return m_notificationCache;
}
@@ -403,7 +401,6 @@ Returns:
*/
void DataStore::saveNotifications()
{
saveRecords<Notification, SerializedNotification>(m_notifications, m_notificationCache);
}
/*
@@ -10,7 +10,6 @@ Created: 11-June-2026
*/
#include "SharedMemory.h"
#include "Windows.h"
#include "Config.h"
/*
@@ -10,6 +10,8 @@ Date:19-May-2026
#include <stdexcept>
#include "AuthenticationManagementService.h"
#include "User.h"
#include "Utility.h"
#include "DataStoreLockGuard.h"
User* AuthenticationManagementService::m_authenticatedUser = nullptr;
@@ -24,11 +26,12 @@ Return type: bool - true if login successful, false otherwise
*/
bool AuthenticationManagementService::login(const std::string& username, const std::string& password)
{
util::Map<std::string, User*> users = m_dataStore.getUsers();
int usersMapSize = users.getSize();
for (int index = 0; index < usersMapSize; index++)
DataStoreLockGuard lock(m_dataStore);
auto& trackedUserMap = m_dataStore.getUsers();
int trackedUserMapSize = trackedUserMap.getSize();
for (int index = 0; index < trackedUserMapSize; index++)
{
User* user = users.getValueAt(index);
User* user = trackedUserMap.getValueAt(index).data;
if (username == user->getUserName())
{
if (password == user->getPassword())
@@ -74,9 +77,18 @@ Return type: void
*/
void AuthenticationManagementService::changePassword(const std::string& newPassword)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
int index = trackedUsersMap.find(m_authenticatedUser->getId());
if (index == -1)
{
throw std::runtime_error("User does not exist!\n");
}
if (m_authenticatedUser == nullptr)
{
throw std::runtime_error("There is no user currently logged in!");
}
m_authenticatedUser->setPassword(newPassword);
trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveUsers();
}
@@ -20,9 +20,6 @@ Date:19-May-2026
#include "UserManagementService.h"
#include "Vector.h"
#include "Validator.h"
#include "Utility.h"
#include "TrackedRecord.h"
#include "DataStoreLockGuard.h"
/*
Function: ensureAdminExists
@@ -34,13 +31,12 @@ 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).data;
User* user = usersMap.getValueAt(index);
if (user && user->getUserType() == util::UserType::ADMIN)
{
isAdminFound = true;
@@ -77,9 +73,7 @@ void UserManagementService::createUser(const std::string& username, const std::s
InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService;
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
auto usersMap = util::getObjects(trackedUsersMap);
auto& usersMap = m_dataStore.getUsers();
if (util::isUsernameDuplicate(username, usersMap))
{
throw std::runtime_error("Username already exists");
@@ -93,14 +87,13 @@ void UserManagementService::createUser(const std::string& username, const std::s
throw std::runtime_error("Phone already exists");
}
User* newUser = Factory::getObject<User>(username, password, name, phone, email, type);
trackedUsersMap.insert(newUser->getId(), util::createNewRecord(newUser));
usersMap.insert(newUser->getId(), newUser);
paymentManagementService.attach(newUser);
serviceManagementService.attach(newUser);
if (newUser->getUserType() == util::UserType::ADMIN)
{
inventoryManagementService.attach(newUser);
}
m_dataStore.saveUsers();
}
/*
@@ -114,24 +107,19 @@ Return type: void
*/
void UserManagementService::updateUserDetails(const std::string& userID, const std::string& email, const std::string& phone)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
auto usersMap = util::getObjects(trackedUsersMap);
int index = trackedUsersMap.find(userID);
auto& usersMap = m_dataStore.getUsers();
int index = usersMap.find(userID);
if (index == -1)
{
throw std::runtime_error("User does not exist!\n");
}
User* user = trackedUsersMap.getValueAt(index).data;
bool isModified = false;
User* user = usersMap.getValueAt(index);
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())
{
@@ -139,14 +127,9 @@ void UserManagementService::updateUserDetails(const std::string& userID, const s
{
throw std::runtime_error("Phone number already exists!\n");
}
}
user->setEmail(email);
user->setPhone(phone);
isModified = true;
}
if (isModified)
{
trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveUsers();
}
}
/*
@@ -161,13 +144,12 @@ Throws:
*/
util::Vector<Notification*> UserManagementService::getUserNotifications(const std::string& userID)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
if (trackedUsersMap.find(userID) == -1)
auto& usersMap = m_dataStore.getUsers();
if (usersMap.find(userID) == -1)
{
throw std::runtime_error("No user found with given UserID");
}
User* user = trackedUsersMap[userID].data;
User* user = usersMap[userID];
if (user)
{
auto& notifications = user->getNotifications();
@@ -187,41 +169,97 @@ util::Vector<Notification*> UserManagementService::getUserNotifications(const st
/*
Function: deleteNotification
Description: Marks a specific notification associated with a given user
as inactive.
Description: Deletes a specific notification associated with a given user ID.
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)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
auto& trackedNotificationsMap = m_dataStore.getNotifications();
int userIndex = trackedUsersMap.find(userID);
if (userIndex == -1)
auto& usersMap = m_dataStore.getUsers();
if (usersMap.find(userID) == -1)
{
throw std::runtime_error("No user found with given UserID");
}
User* user = trackedUsersMap.getValueAt(userIndex).data;
User* user = usersMap[userID];
auto& notifications = user->getNotifications();
if (notifications.find(notificationID) == -1)
{
throw std::runtime_error("No notification found with given NotificationID");
}
int notificationIndex = trackedNotificationsMap.find(notificationID);
if (notificationIndex == -1)
notifications.remove(notificationID);
}
/*
Function: loadUsers
Description: Loads users and notifications from persistent storage into the datastore.
Validates that each notifications 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<User> userFileManager(config::file::USER_FILE);
util::FileManager<Notification> 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++)
{
throw std::runtime_error("No notification found with given NotificationID");
users[usersMap.getKeyAt(index)] = usersMap.getValueAt(index);
}
notifications[notificationID]->setState(util::State::INACTIVE);
trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED;
m_dataStore.saveNotifications();
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<User> userFileManager(config::file::USER_FILE);
util::FileManager<Notification> notificationFileManager(config::file::NOTIFICATION_FILE);
auto& users = m_dataStore.getUsers();
util::Map<std::string, Notification*> 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);
}
/*
@@ -232,9 +270,7 @@ Return type: util::Map<std::string, User*>
*/
util::Map<std::string, User*> UserManagementService::getUsers()
{
DataStoreLockGuard lock(m_dataStore);
auto users = util::getObjects(m_dataStore.getUsers());
return users;
return m_dataStore.getUsers();
}
/*
@@ -245,12 +281,10 @@ Return type: User*
*/
User* UserManagementService::getUser(const std::string& userID)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
int index = trackedUsersMap.find(userID);
int index = m_dataStore.getUsers().find(userID);
if (index != -1)
{
return trackedUsersMap.getValueAt(index).data;
return m_dataStore.getUsers().getValueAt(index);
}
return nullptr;
}
@@ -266,12 +300,10 @@ void UserManagementService::removeUser(const std::string& userID)
InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService;
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
int index = trackedUsersMap.find(userID);
int index = m_dataStore.getUsers().find(userID);
if (index != -1)
{
User* user = trackedUsersMap.getValueAt(index).data;
User* user = m_dataStore.getUsers().getValueAt(index);
if (user != nullptr)
{
if (user->getUserType() == util::UserType::CUSTOMER)
@@ -282,37 +314,21 @@ void UserManagementService::removeUser(const std::string& userID)
{
serviceManagementService.cancelTechnicianJobs(userID);
}
user->setState(util::State::INACTIVE);
inventoryManagementService.detach(user);
paymentManagementService.detach(user);
serviceManagementService.detach(user);
user->setState(util::State::INACTIVE);
trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveUsers();
}
}
}
/*
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<std::string, User*>:
Collection of active users matching the specified type,
keyed by user ID.
*/
util::Map<std::string, User*> UserManagementService::getUsers(util::UserType type)
{
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
util::Map<std::string, User*> currentUsers = util::getObjects(trackedUsersMap);
util::Map<std::string, User*>& currentUsers = m_dataStore.getUsers();
util::Map<std::string, User*> filteredUsersMap;
for (int index = 0; index < currentUsers.getSize(); index++)
for (int iterator = 0; iterator < currentUsers.getSize(); iterator++)
{
User* currentUser = currentUsers.getValueAt(index);
User* currentUser = currentUsers.getValueAt(iterator);
if (currentUser && currentUser->getState() == util::State::ACTIVE && currentUser->getUserType() == type)
{
filteredUsersMap.insert(currentUser->getId(), currentUser);
@@ -31,4 +31,6 @@ public:
util::Vector<Notification*> getUserNotifications(const std::string& userID);
void deleteNotification(const std::string& notificationID, const std::string& userID);
void ensureAdminExists();
void loadUsers();
void saveUsers();
};
@@ -27,11 +27,8 @@ void UserInterface::run()
{
try
{
if (!m_controller.initialize())
{
std::cout << "Error: Failed to initialize the system!";
return;
}
m_controller.loadSystemData();
m_controller.runSystemChecks();
bool isMenuActive = true;
while (isMenuActive)
{
@@ -52,7 +49,7 @@ void UserInterface::run()
util::pressEnter();
}
}
m_controller.shutdown();
m_controller.saveSystemData();
}
catch (const std::invalid_argument& exception)
{