Compare commits

..

3 Commits

Author SHA1 Message Date
joelthomastrenser b5c8b1ee9b Implement review fixes 2026-06-15 14:46:37 +05:30
joelthomastrenser 89fc662181 Implement Service Refactoring
<UserStory> 1960: Service Refactoring </UserStory>

UserStory #1960

<Changes>
1. Replaced the Controller load/save workflow with initialize() and shutdown() methods.
2. Moved startup checks such as admin verification, low stock alerts, and payment reminders into Controller::initialize().
3. Updated UserInterface to use the new Controller initialization and shutdown flow.
4. Updated DataStore::getUsers() and DataStore::getNotifications() to load data directly from shared memory.
5. Added logic to rebuild user notification mappings when user data is loaded from shared memory.
6. Implemented user and notification persistence through DataStore::saveUsers() and DataStore::saveNotifications().
7. Updated UserManagementService to use TrackedRecord-based shared memory records.
8. Added DataStore locking and unlocking to user management operations for synchronization.
9. Updated createUser(), updateUserDetails(), removeUser(), and deleteNotification() to persist changes through shared memory.
10. Removed legacy loadUsers() and saveUsers() implementations from UserManagementService.
11. Added required header dependencies for shared memory support.
</Changes>

<Test>
N/A
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
2026-06-12 13:04:23 +05:30
joelthomastrenser 929f609f24 Implement Model Refactoring
<UserStory> 1959: Model Refactoring </UserStory>

UserStory #1959

<Changes>
1. Replaced CSV-based User serialization and deserialization with SerializedUser record-based serialization for shared memory storage.
2. Implemented User::serialize() to convert User objects into fixed-size SerializedUser structures containing user details and enum values.
3. Implemented User::deserialize() to reconstruct User objects directly from SerializedUser records.
4. Updated User class interfaces to use SerializedUser types instead of std::string serialization APIs.
5. Removed legacy CSV serialization support, including CSV parsing logic and header generation functionality.
6. Added SerializedUser dependencies through SerializedRecords.h inclusion and forward declaration support.
</Changes>

<Test>
N/A
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
2026-06-12 03:19:43 +05:30
13 changed files with 334 additions and 297 deletions
@@ -588,63 +588,37 @@ void Controller::configureNotifications(bool paymentNotifications, bool serviceN
} }
/* /*
Function: loadSystemData Function: initialize
Description: Loads all system data from persistent storage into memory. Description: Initializes the system and run system checks to ensure critical configurations, such as verifying admin existence.
Invokes the respective management services to load users, inventory items, services,
combo packages, service bookings, job cards, invoices, and observers.
Parameters: Parameters:
- None - None
Returns: Returns:
- void - bool
*/ */
void Controller::loadSystemData() bool Controller::initialize()
{ {
m_userManagementService.loadUsers(); auto& dataStore = DataStore::getInstance();
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();
}
/* if (!dataStore.initialize())
Function: saveSystemData {
Description: Saves all system data from memory back to persistent storage. return false;
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_userManagementService.ensureAdminExists();
m_inventoryManagementService.sendLowStockAlerts(); m_inventoryManagementService.sendLowStockAlerts();
m_paymentManagementService.sendPaymentReminders(); 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();
}
@@ -70,7 +70,6 @@ public:
util::Vector<const Notification*> getNotifications(); util::Vector<const Notification*> getNotifications();
void deleteNotification(const std::string& notificationID); void deleteNotification(const std::string& notificationID);
void configureNotifications(bool paymentNotifications, bool serviceNotifications); void configureNotifications(bool paymentNotifications, bool serviceNotifications);
void loadSystemData(); bool initialize();
void saveSystemData(); void shutdown();
void runSystemChecks();
}; };
@@ -257,6 +257,8 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<Notification>>& DataStore::getNotifications() util::Map<std::string, TrackedRecord<Notification>>& DataStore::getNotifications()
{ {
auto notifications = loadRecords<Notification, SerializedNotification>(m_notifications);
refreshCache(m_notificationCache, notifications);
return m_notificationCache; return m_notificationCache;
} }
@@ -296,8 +298,6 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems() util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems()
{ {
auto inventoryItems = loadRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems);
refreshCache(m_inventoryItemCache, inventoryItems);
return m_inventoryItemCache; return m_inventoryItemCache;
} }
@@ -389,6 +389,8 @@ Returns:
*/ */
void DataStore::saveUsers() void DataStore::saveUsers()
{ {
saveRecords<User, SerializedUser>(m_users, m_userCache);
saveNotifications();
} }
/* /*
@@ -401,6 +403,7 @@ Returns:
*/ */
void DataStore::saveNotifications() void DataStore::saveNotifications()
{ {
saveRecords<Notification, SerializedNotification>(m_notifications, m_notificationCache);
} }
/* /*
@@ -437,7 +440,6 @@ Returns:
*/ */
void DataStore::saveInventoryItems() void DataStore::saveInventoryItems()
{ {
saveRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems, m_inventoryItemCache);
} }
/* /*
@@ -10,6 +10,7 @@ Created: 11-June-2026
*/ */
#include "SharedMemory.h" #include "SharedMemory.h"
#include "Windows.h"
#include "Config.h" #include "Config.h"
/* /*
@@ -8,7 +8,6 @@ Date: 19-May-2026
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "SerializedRecords.h"
#include "Factory.h" #include "Factory.h"
#include "StringHelper.h" #include "StringHelper.h"
#include "InventoryItem.h" #include "InventoryItem.h"
@@ -28,8 +27,7 @@ InventoryItem::InventoryItem()
: m_id("IIM" + std::to_string(++m_uid)), : m_id("IIM" + std::to_string(++m_uid)),
m_quantity(0), m_quantity(0),
m_status(util::State::ACTIVE), m_status(util::State::ACTIVE),
m_price(0.0) { m_price(0.0) {}
}
/* /*
Function: InventoryItem Function: InventoryItem
@@ -47,8 +45,7 @@ InventoryItem::InventoryItem(const std::string& partName, int quantity, double p
m_partName(partName), m_partName(partName),
m_quantity(quantity), m_quantity(quantity),
m_status(util::State::ACTIVE), m_status(util::State::ACTIVE),
m_price(price) { m_price(price) {}
}
/* /*
Function: InventoryItem (parameterized constructor with ID) Function: InventoryItem (parameterized constructor with ID)
@@ -209,37 +206,73 @@ void InventoryItem::setState(util::State status)
/* /*
Function: serialize Function: serialize
Description: Serializes the InventoryItem object into a SerializedInventoryItem record. Description: Serializes the inventory item into a CSV-formatted string.
Parameters: Parameters:
- None - None
Returns: Returns:
- SerializedInventoryItem: Serialized representation of the inventory item - std::string: Serialized inventory item record
*/ */
SerializedInventoryItem InventoryItem::serialize() const std::string InventoryItem::serialize() const
{ {
SerializedInventoryItem serialized = {}; std::ostringstream serializedInventoryItem;
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); serializedInventoryItem << m_id << ','
strcpy_s(serialized.partName, sizeof(serialized.partName), m_partName.c_str()); << m_partName << ','
serialized.quantity = m_quantity; << m_quantity << ','
serialized.price = m_price; << m_price << ','
serialized.status = m_status; << util::getStateString(m_status);
return serialized; return serializedInventoryItem.str();
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a SerializedInventoryItem record into an InventoryItem object. Description: Deserializes a CSV-formatted string into an InventoryItem object.
Parameters: Parameters:
- serializedInventoryItem: const SerializedInventoryItem&, serialized inventory item record - record: const std::string&, serialized inventory item record
Returns: Returns:
- InventoryItem*: Pointer to the deserialized InventoryItem object - InventoryItem*: Pointer to the deserialized InventoryItem object
Throws:
- std::runtime_error if data is invalid
*/ */
InventoryItem* InventoryItem::deserialize(const SerializedInventoryItem& serializedInventoryItem) InventoryItem* InventoryItem::deserialize(const std::string& record)
{ {
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<InventoryItem>( return Factory::getObject<InventoryItem>(
serializedInventoryItem.id, id,
serializedInventoryItem.partName, partName,
serializedInventoryItem.quantity, quantity,
serializedInventoryItem.price, price,
serializedInventoryItem.status); 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";
} }
@@ -6,12 +6,11 @@ Author: Trenser
Date: 19-May-2026 Date: 19-May-2026
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include "Enums.h" #include "Enums.h"
struct SerializedInventoryItem;
class InventoryItem class InventoryItem
{ {
private: private:
@@ -35,6 +34,7 @@ public:
void setQuantity(int quantity); void setQuantity(int quantity);
void setPrice(double price); void setPrice(double price);
void setState(util::State status); void setState(util::State status);
SerializedInventoryItem serialize() const; std::string serialize() const;
static InventoryItem* deserialize(const SerializedInventoryItem&); static InventoryItem* deserialize(const std::string&);
static std::string getHeaders();
}; };
@@ -8,6 +8,7 @@ Date: 19-May-2026
*/ */
#include <sstream> #include <sstream>
#include "SerializedRecords.h"
#include "User.h" #include "User.h"
#include "Notification.h" #include "Notification.h"
#include "Enums.h" #include "Enums.h"
@@ -28,7 +29,8 @@ Returns:
User::User() User::User()
: m_id("USR" + std::to_string(++m_uid)), : m_id("USR" + std::to_string(++m_uid)),
m_type(util::UserType::CUSTOMER), m_type(util::UserType::CUSTOMER),
m_status(util::State::ACTIVE) {} m_status(util::State::ACTIVE) {
}
/* /*
Function: User Function: User
@@ -51,7 +53,8 @@ User::User(const std::string& userName, const std::string& password, const std::
m_phone(phone), m_phone(phone),
m_email(email), m_email(email),
m_type(role), m_type(role),
m_status(util::State::ACTIVE) {} m_status(util::State::ACTIVE) {
}
/* /*
Function: User (parameterized constructor with ID) Function: User (parameterized constructor with ID)
@@ -324,68 +327,43 @@ void User::setState(util::State status)
/* /*
Function: serialize Function: serialize
Description: Serializes the user into a CSV-formatted string. Description: Serializes the User object into a SerializedUser record.
Parameters: Parameters:
- None - None
Returns: 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 serialized = {};
serializedUser << m_id << ',' strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str());
<< m_userName << ',' strcpy_s(serialized.username, sizeof(serialized.username), m_userName.c_str());
<< m_password << ',' strcpy_s(serialized.password, sizeof(serialized.password), m_password.c_str());
<< m_name << ',' strcpy_s(serialized.name, sizeof(serialized.name), m_name.c_str());
<< m_phone << ',' strcpy_s(serialized.phone, sizeof(serialized.phone), m_phone.c_str());
<< m_email << ',' strcpy_s(serialized.email, sizeof(serialized.email), m_email.c_str());
<< util::getUserTypeString(m_type) << ',' serialized.userType = m_type;
<< util::getStateString(m_status); serialized.status = m_status;
return serializedUser.str(); return serialized;
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a CSV-formatted string into a User object. Description: Deserializes a SerializedUser record into a User object.
Parameters: Parameters:
- record: const std::string&, serialized user record - serializedUser: const SerializedUser&, serialized user record
Returns: Returns:
- User*: Pointer to the deserialized User object - 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; return Factory::getObject<User>(
std::string userTypeString, stateString; serializedUser.id,
std::istringstream serializedUser(record); serializedUser.username,
getline(serializedUser, id, ','); serializedUser.password,
getline(serializedUser, username, ','); serializedUser.name,
getline(serializedUser, password, ','); serializedUser.phone,
getline(serializedUser, name, ','); serializedUser.email,
getline(serializedUser, phone, ','); serializedUser.userType,
getline(serializedUser, email, ','); serializedUser.status);
getline(serializedUser, userTypeString, ',');
getline(serializedUser, stateString);
util::UserType userType = util::getUserType(userTypeString);
util::State status = util::getState(stateString);
return Factory::getObject<User>(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";
} }
@@ -14,6 +14,7 @@ Date: 19-May-2026
#include "Enums.h" #include "Enums.h"
class Notification; class Notification;
struct SerializedUser;
class User : public Observer class User : public Observer
{ {
@@ -51,7 +52,6 @@ public:
void addNotification(Notification* notification) override; void addNotification(Notification* notification) override;
void setRole(util::UserType role); void setRole(util::UserType role);
void setState(util::State status); void setState(util::State status);
std::string serialize() const; SerializedUser serialize() const;
static User* deserialize(const std::string&); static User* deserialize(const SerializedUser& serializedUser);
static std::string getHeaders();
}; };
@@ -19,7 +19,7 @@ Date: 22-May-2026
#include "User.h" #include "User.h"
#include "Utility.h" #include "Utility.h"
#include "Vector.h" #include "Vector.h"
#include "DataStoreLockGuard.h"
util::Map<std::string, User*> InventoryManagementService::m_observers{}; util::Map<std::string, User*> InventoryManagementService::m_observers{};
@@ -58,19 +58,18 @@ Returns:
*/ */
void InventoryManagementService::sendLowStockAlerts() void InventoryManagementService::sendLowStockAlerts()
{ {
DataStoreLockGuard lock(m_dataStore); auto& inventoryItems = m_dataStore.getInventoryItems();
auto& trackedInventoryItemsMap = m_dataStore.getInventoryItems(); if (inventoryItems.isEmpty())
auto& trackedUserMap = m_dataStore.getUsers();
if (trackedInventoryItemsMap.isEmpty())
{ {
return; return;
} }
int inventoryItemsSize = trackedInventoryItemsMap.getSize(); int inventoryItemsSize = inventoryItems.getSize();
int usersMapSize = trackedUserMap.getSize(); auto& usersMap = m_dataStore.getUsers();
int usersMapSize = usersMap.getSize();
util::Vector<User*> adminUsers; util::Vector<User*> adminUsers;
for (int index = 0; index < usersMapSize; index++) for (int index = 0; index < usersMapSize; index++)
{ {
User* user = trackedUserMap.getValueAt(index).data; User* user = usersMap.getValueAt(index);
if (user->getUserType() == util::UserType::ADMIN) if (user->getUserType() == util::UserType::ADMIN)
{ {
adminUsers.push_back(user); adminUsers.push_back(user);
@@ -83,7 +82,7 @@ void InventoryManagementService::sendLowStockAlerts()
} }
for (int index = 0; index < inventoryItemsSize; index++) for (int index = 0; index < inventoryItemsSize; index++)
{ {
InventoryItem* inventoryItem = trackedInventoryItemsMap.getValueAt(index).data; InventoryItem* inventoryItem = inventoryItems.getValueAt(index);
if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD) if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD)
{ {
sendLowStockAlertsToAdmins(*this, inventoryItem, adminUsers); sendLowStockAlertsToAdmins(*this, inventoryItem, adminUsers);
@@ -91,6 +90,95 @@ void InventoryManagementService::sendLowStockAlerts()
} }
} }
/*
Function: getObserverIDs
Description: Retrieves the IDs of all observers currently attached to the
InventoryManagementService.
Parameters:
- None
Returns:
- util::Vector<std::string>: Vector of observer user IDs
*/
util::Vector<std::string> InventoryManagementService::getObserverIDs()
{
util::Vector<std::string> 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<InventoryItem> 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<InventoryItem> 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 Function: addInventoryItem
Description: Creates a new inventory item using the Factory and inserts it Description: Creates a new inventory item using the Factory and inserts it
@@ -102,11 +190,8 @@ Return type: void
*/ */
void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price) void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price)
{ {
DataStoreLockGuard lock(m_dataStore);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
InventoryItem* newItem = Factory::getObject<InventoryItem>(partName, quantity, price); InventoryItem* newItem = Factory::getObject<InventoryItem>(partName, quantity, price);
trackedInventoryItemMap.insert(newItem->getId(), util::createNewRecord(newItem)); m_dataStore.getInventoryItems().insert(newItem->getId(), newItem);
m_dataStore.saveInventoryItems();
} }
/* /*
@@ -118,22 +203,16 @@ Return type: void
*/ */
void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity) void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity)
{ {
DataStoreLockGuard lock(m_dataStore); int index = m_dataStore.getInventoryItems().find(selectedItemId);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); if (index != -1)
int index = trackedInventoryItemMap.find(selectedItemId); {
if (index == -1) InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index);
{ if (item != nullptr)
throw std::runtime_error("Inventory update failed: Item ID '" + selectedItemId + "' not found."); {
} int totalQuantity = item->getQuantity() + quantity;
InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data; item->setQuantity(totalQuantity);
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();
} }
/* /*
@@ -144,10 +223,7 @@ Return type: util::Map<std::string, InventoryItem*>
*/ */
util::Map<std::string, InventoryItem*> InventoryManagementService::getInventoryItems() util::Map<std::string, InventoryItem*> InventoryManagementService::getInventoryItems()
{ {
DataStoreLockGuard lock(m_dataStore); return m_dataStore.getInventoryItems();
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
auto inventoryMap = util::getObjects(trackedInventoryItemMap);
return inventoryMap;
} }
/* /*
@@ -158,21 +234,15 @@ Return type: void
*/ */
void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID) void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID)
{ {
DataStoreLockGuard lock(m_dataStore); int index = m_dataStore.getInventoryItems().find(inventoryItemID);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); if (index != -1)
int index = trackedInventoryItemMap.find(inventoryItemID); {
if (index == -1) InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index);
{ if (item != nullptr)
throw std::runtime_error("Inventory removal failed: Item ID '" + inventoryItemID + "' not found."); {
} item->setState(util::State::INACTIVE);
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();
} }
/* /*
@@ -183,19 +253,12 @@ Return type: InventoryItem*
*/ */
InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID) InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID)
{ {
DataStoreLockGuard lock(m_dataStore); int index = m_dataStore.getInventoryItems().find(inventoryItemID);
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems(); if (index != -1)
int index = trackedInventoryItemMap.find(inventoryItemID);
if (index == -1)
{ {
return nullptr; return m_dataStore.getInventoryItems().getValueAt(index);
} }
InventoryItem* inventoryItem = trackedInventoryItemMap.getValueAt(index).data; return nullptr;
if (inventoryItem == nullptr)
{
throw std::runtime_error("Item ID does not exist.");
}
return inventoryItem;
} }
/* /*
@@ -33,6 +33,8 @@ public:
void sendNotification(User* user, const std::string& title, const std::string& message) override; void sendNotification(User* user, const std::string& title, const std::string& message) override;
void attach(User* user) override; void attach(User* user) override;
void detach(User* user) override; void detach(User* user) override;
void loadInventoryItems();
void saveInventoryItems();
void loadObservers(); void loadObservers();
void saveObservers(); void saveObservers();
}; };
@@ -20,6 +20,9 @@ Date:19-May-2026
#include "UserManagementService.h" #include "UserManagementService.h"
#include "Vector.h" #include "Vector.h"
#include "Validator.h" #include "Validator.h"
#include "Utility.h"
#include "TrackedRecord.h"
#include "DataStoreLockGuard.h"
/* /*
Function: ensureAdminExists Function: ensureAdminExists
@@ -31,12 +34,13 @@ Return type: void
*/ */
void UserManagementService::ensureAdminExists() void UserManagementService::ensureAdminExists()
{ {
DataStoreLockGuard lock(m_dataStore);
auto& usersMap = m_dataStore.getUsers(); auto& usersMap = m_dataStore.getUsers();
int usersMapSize = usersMap.getSize(); int usersMapSize = usersMap.getSize();
bool isAdminFound = false; bool isAdminFound = false;
for (int index = 0; index < usersMapSize; index++) 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) if (user && user->getUserType() == util::UserType::ADMIN)
{ {
isAdminFound = true; isAdminFound = true;
@@ -73,7 +77,9 @@ void UserManagementService::createUser(const std::string& username, const std::s
InventoryManagementService inventoryManagementService; InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService; 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)) if (util::isUsernameDuplicate(username, usersMap))
{ {
throw std::runtime_error("Username already exists"); throw std::runtime_error("Username already exists");
@@ -87,13 +93,14 @@ void UserManagementService::createUser(const std::string& username, const std::s
throw std::runtime_error("Phone already exists"); throw std::runtime_error("Phone already exists");
} }
User* newUser = Factory::getObject<User>(username, password, name, phone, email, type); User* newUser = Factory::getObject<User>(username, password, name, phone, email, type);
usersMap.insert(newUser->getId(), newUser); trackedUsersMap.insert(newUser->getId(), util::createNewRecord(newUser));
paymentManagementService.attach(newUser); paymentManagementService.attach(newUser);
serviceManagementService.attach(newUser); serviceManagementService.attach(newUser);
if (newUser->getUserType() == util::UserType::ADMIN) if (newUser->getUserType() == util::UserType::ADMIN)
{ {
inventoryManagementService.attach(newUser); inventoryManagementService.attach(newUser);
} }
m_dataStore.saveUsers();
} }
/* /*
@@ -107,19 +114,24 @@ Return type: void
*/ */
void UserManagementService::updateUserDetails(const std::string& userID, const std::string& email, const std::string& phone) void UserManagementService::updateUserDetails(const std::string& userID, const std::string& email, const std::string& phone)
{ {
auto& usersMap = m_dataStore.getUsers(); DataStoreLockGuard lock(m_dataStore);
int index = usersMap.find(userID); auto& trackedUsersMap = m_dataStore.getUsers();
auto usersMap = util::getObjects(trackedUsersMap);
int index = trackedUsersMap.find(userID);
if (index == -1) if (index == -1)
{ {
throw std::runtime_error("User does not exist!\n"); 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 (email != user->getEmail())
{ {
if (util::isEmailDuplicate(email, usersMap)) if (util::isEmailDuplicate(email, usersMap))
{ {
throw std::runtime_error("Email already exists!\n"); throw std::runtime_error("Email already exists!\n");
} }
user->setEmail(email);
isModified = true;
} }
if (phone != user->getPhone()) if (phone != user->getPhone())
{ {
@@ -127,9 +139,14 @@ void UserManagementService::updateUserDetails(const std::string& userID, const s
{ {
throw std::runtime_error("Phone number already exists!\n"); throw std::runtime_error("Phone number already exists!\n");
} }
user->setPhone(phone);
isModified = true;
}
if (isModified)
{
trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveUsers();
} }
user->setEmail(email);
user->setPhone(phone);
} }
/* /*
@@ -144,12 +161,13 @@ Throws:
*/ */
util::Vector<Notification*> UserManagementService::getUserNotifications(const std::string& userID) util::Vector<Notification*> UserManagementService::getUserNotifications(const std::string& userID)
{ {
auto& usersMap = m_dataStore.getUsers(); DataStoreLockGuard lock(m_dataStore);
if (usersMap.find(userID) == -1) auto& trackedUsersMap = m_dataStore.getUsers();
if (trackedUsersMap.find(userID) == -1)
{ {
throw std::runtime_error("No user found with given UserID"); throw std::runtime_error("No user found with given UserID");
} }
User* user = usersMap[userID]; User* user = trackedUsersMap[userID].data;
if (user) if (user)
{ {
auto& notifications = user->getNotifications(); auto& notifications = user->getNotifications();
@@ -169,97 +187,41 @@ util::Vector<Notification*> UserManagementService::getUserNotifications(const st
/* /*
Function: deleteNotification 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: Parameters:
- notificationID: The unique ID of the notification to be deleted. - notificationID: The unique ID of the notification to be deleted.
- userID: The unique ID of the user whose notification is to be deleted. - userID: The unique ID of the user whose notification is to be deleted.
Returns: Returns:
- void - void
Throws: 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) void UserManagementService::deleteNotification(const std::string& notificationID, const std::string& userID)
{ {
auto& usersMap = m_dataStore.getUsers(); DataStoreLockGuard lock(m_dataStore);
if (usersMap.find(userID) == -1) 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"); throw std::runtime_error("No user found with given UserID");
} }
User* user = usersMap[userID]; User* user = trackedUsersMap.getValueAt(userIndex).data;
auto& notifications = user->getNotifications(); auto& notifications = user->getNotifications();
if (notifications.find(notificationID) == -1) if (notifications.find(notificationID) == -1)
{ {
throw std::runtime_error("No notification found with given NotificationID"); throw std::runtime_error("No notification found with given NotificationID");
} }
notifications.remove(notificationID); int notificationIndex = trackedNotificationsMap.find(notificationID);
} if (notificationIndex == -1)
{
/* throw std::runtime_error("No notification found with given NotificationID");
Function: loadUsers }
Description: Loads users and notifications from persistent storage into the datastore. notifications[notificationID]->setState(util::State::INACTIVE);
Validates that each notifications recipient exists and attaches the trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED;
notification to the corresponding user. m_dataStore.saveNotifications();
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++)
{
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<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);
} }
/* /*
@@ -270,7 +232,9 @@ Return type: util::Map<std::string, User*>
*/ */
util::Map<std::string, User*> UserManagementService::getUsers() util::Map<std::string, User*> UserManagementService::getUsers()
{ {
return m_dataStore.getUsers(); DataStoreLockGuard lock(m_dataStore);
auto users = util::getObjects(m_dataStore.getUsers());
return users;
} }
/* /*
@@ -281,10 +245,12 @@ Return type: User*
*/ */
User* UserManagementService::getUser(const std::string& userID) 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) if (index != -1)
{ {
return m_dataStore.getUsers().getValueAt(index); return trackedUsersMap.getValueAt(index).data;
} }
return nullptr; return nullptr;
} }
@@ -300,10 +266,12 @@ void UserManagementService::removeUser(const std::string& userID)
InventoryManagementService inventoryManagementService; InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService; ServiceManagementService serviceManagementService;
int index = m_dataStore.getUsers().find(userID); DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
int index = trackedUsersMap.find(userID);
if (index != -1) if (index != -1)
{ {
User* user = m_dataStore.getUsers().getValueAt(index); User* user = trackedUsersMap.getValueAt(index).data;
if (user != nullptr) if (user != nullptr)
{ {
if (user->getUserType() == util::UserType::CUSTOMER) if (user->getUserType() == util::UserType::CUSTOMER)
@@ -314,21 +282,37 @@ void UserManagementService::removeUser(const std::string& userID)
{ {
serviceManagementService.cancelTechnicianJobs(userID); serviceManagementService.cancelTechnicianJobs(userID);
} }
user->setState(util::State::INACTIVE);
inventoryManagementService.detach(user); inventoryManagementService.detach(user);
paymentManagementService.detach(user); paymentManagementService.detach(user);
serviceManagementService.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) util::Map<std::string, User*> UserManagementService::getUsers(util::UserType type)
{ {
util::Map<std::string, User*>& currentUsers = m_dataStore.getUsers(); DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
util::Map<std::string, User*> currentUsers = util::getObjects(trackedUsersMap);
util::Map<std::string, User*> filteredUsersMap; util::Map<std::string, User*> 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) if (currentUser && currentUser->getState() == util::State::ACTIVE && currentUser->getUserType() == type)
{ {
filteredUsersMap.insert(currentUser->getId(), currentUser); filteredUsersMap.insert(currentUser->getId(), currentUser);
@@ -31,6 +31,4 @@ public:
util::Vector<Notification*> getUserNotifications(const std::string& userID); util::Vector<Notification*> getUserNotifications(const std::string& userID);
void deleteNotification(const std::string& notificationID, const std::string& userID); void deleteNotification(const std::string& notificationID, const std::string& userID);
void ensureAdminExists(); void ensureAdminExists();
void loadUsers();
void saveUsers();
}; };
@@ -27,8 +27,11 @@ void UserInterface::run()
{ {
try try
{ {
m_controller.loadSystemData(); if (!m_controller.initialize())
m_controller.runSystemChecks(); {
std::cout << "Error: Failed to initialize the system!";
return;
}
bool isMenuActive = true; bool isMenuActive = true;
while (isMenuActive) while (isMenuActive)
{ {
@@ -49,7 +52,7 @@ void UserInterface::run()
util::pressEnter(); util::pressEnter();
} }
} }
m_controller.saveSystemData(); m_controller.shutdown();
} }
catch (const std::invalid_argument& exception) catch (const std::invalid_argument& exception)
{ {