Compare commits

..

2 Commits

Author SHA1 Message Date
joelthomastrenser 4f9b92639e 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-11 19:59:10 +05:30
joelthomastrenser e5787dfb98 Impelement datastore shared memory codebase
Changes:
- Added SharedMemory module for file-backed memory-mapped storage
- Added MappingInfo, FileHeader, RecordState and TrackedRecord infrastructure
- Replaced CSV-based serialization with binary struct serialization
- Added DataStore initialization and shutdown lifecycle management
- Added datastore mutex synchronization for multi-process access
- Added shared-memory mapping configuration for all datastore entities
- Added generic loadRecords and saveRecords template infrastructure
- Added automatic datastore directory creation and .dat file storage
- Updated configuration to use binary datastore files and initial capacities
- Added enum underlying types for serialization compatibility
- Added password masking support for login, registration and password change flows
- Added Visual Studio project configuration for shared-memory components
- Prepared datastore for multi-process shared-memory persistence
2026-06-11 19:07:03 +05:30
11 changed files with 261 additions and 421 deletions
@@ -157,7 +157,6 @@
<ClInclude Include="core\patterns\Observer.h" /> <ClInclude Include="core\patterns\Observer.h" />
<ClInclude Include="core\patterns\Subject.h" /> <ClInclude Include="core\patterns\Subject.h" />
<ClInclude Include="datastores\DataStore.h" /> <ClInclude Include="datastores\DataStore.h" />
<ClInclude Include="datastores\DataStoreLockGuard.h" />
<ClInclude Include="datastores\sharedmemory\FileHeader.h" /> <ClInclude Include="datastores\sharedmemory\FileHeader.h" />
<ClInclude Include="datastores\sharedmemory\MappingInfo.h" /> <ClInclude Include="datastores\sharedmemory\MappingInfo.h" />
<ClInclude Include="datastores\sharedmemory\RecordState.h" /> <ClInclude Include="datastores\sharedmemory\RecordState.h" />
@@ -588,37 +588,63 @@ void Controller::configureNotifications(bool paymentNotifications, bool serviceN
} }
/* /*
Function: initialize Function: loadSystemData
Description: Initializes the system and run system checks to ensure critical configurations, such as verifying admin existence. Description: Loads all system data from persistent storage into memory.
Parameters: Invokes the respective management services to load users, inventory items, services,
- None combo packages, service bookings, job cards, invoices, and observers.
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
Parameters: Parameters:
- None - None
Returns: Returns:
- void - void
*/ */
void Controller::shutdown() void Controller::loadSystemData()
{ {
auto& dataStore = DataStore::getInstance(); m_userManagementService.loadUsers();
dataStore.shutdown(); 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(); 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);
bool initialize(); void loadSystemData();
void shutdown(); void saveSystemData();
void runSystemChecks();
}; };
@@ -13,42 +13,9 @@ Date: 19-May-2026
#include "SerializedRecords.h" #include "SerializedRecords.h"
#include "FileHelper.h" #include "FileHelper.h"
/*
Function: DataStore
Description: Constructs the DataStore singleton and initializes
internal handles to their default values.
Parameters:
- None
Returns:
- None
*/
DataStore::DataStore() : DataStore::DataStore() :
m_globalMutex(NULL) {} 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 Function: initialize
Description: Initializes the shared-memory datastore. Description: Initializes the shared-memory datastore.
@@ -226,25 +193,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<User>>: Collection of user records - util::Map<std::string, TrackedRecord<User>>: Collection of user records
*/ */
util::Map<std::string, TrackedRecord<User>>& DataStore::getUsers() util::Map<std::string, TrackedRecord<User>> DataStore::getUsers()
{ {
auto users = loadRecords<User, SerializedUser>(m_users); return util::Map<std::string, TrackedRecord<User>>();
refreshCache(m_userCache, users);
auto& notifications = getNotifications();
int numberOfNotifications = m_notificationCache.getSize();
for (int index = 0; index < numberOfNotifications; index++)
{
Notification* notification = notifications.getValueAt(index).data;
const std::string& recipientUserId = notification->getRecipientUserId();
int userIndex = m_userCache.find(recipientUserId);
if (userIndex == -1)
{
throw std::runtime_error("Invalid recipient user ID");
}
User* user = m_userCache.getValueAt(userIndex).data;
user->addNotification(notification);
}
return m_userCache;
} }
/* /*
@@ -255,11 +206,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<Notification>>: Collection of notification records - util::Map<std::string, TrackedRecord<Notification>>: Collection of notification records
*/ */
util::Map<std::string, TrackedRecord<Notification>>& DataStore::getNotifications() util::Map<std::string, TrackedRecord<Notification>> DataStore::getNotifications()
{ {
auto notifications = loadRecords<Notification, SerializedNotification>(m_notifications); return util::Map<std::string, TrackedRecord<Notification>>();
refreshCache(m_notificationCache, notifications);
return m_notificationCache;
} }
/* /*
@@ -270,9 +219,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<Service>>: Collection of service records - util::Map<std::string, TrackedRecord<Service>>: Collection of service records
*/ */
util::Map<std::string, TrackedRecord<Service>>& DataStore::getServices() util::Map<std::string, TrackedRecord<Service>> DataStore::getServices()
{ {
return m_serviceCache; return util::Map<std::string, TrackedRecord<Service>>();
} }
/* /*
@@ -283,9 +232,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<ComboPackage>>: Collection of combo package records - util::Map<std::string, TrackedRecord<ComboPackage>>: Collection of combo package records
*/ */
util::Map<std::string, TrackedRecord<ComboPackage>>& DataStore::getComboPackages() util::Map<std::string, TrackedRecord<ComboPackage>> DataStore::getComboPackages()
{ {
return m_comboPackageCache; return util::Map<std::string, TrackedRecord<ComboPackage>>();
} }
/* /*
@@ -296,9 +245,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<InventoryItem>>: Collection of inventory item records - util::Map<std::string, TrackedRecord<InventoryItem>>: Collection of inventory item records
*/ */
util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems() util::Map<std::string, TrackedRecord<InventoryItem>> DataStore::getInventoryItems()
{ {
return m_inventoryItemCache; return util::Map<std::string, TrackedRecord<InventoryItem>>();
} }
/* /*
@@ -309,9 +258,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<ServiceBooking>>: Collection of service booking records - util::Map<std::string, TrackedRecord<ServiceBooking>>: Collection of service booking records
*/ */
util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBookings() util::Map<std::string, TrackedRecord<ServiceBooking>> DataStore::getServiceBookings()
{ {
return m_serviceBookingCache; return util::Map<std::string, TrackedRecord<ServiceBooking>>();
} }
/* /*
@@ -322,9 +271,9 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<JobCard>>: Collection of job card records - util::Map<std::string, TrackedRecord<JobCard>>: Collection of job card records
*/ */
util::Map<std::string, TrackedRecord<JobCard>>& DataStore::getJobCards() util::Map<std::string, TrackedRecord<JobCard>> DataStore::getJobCards()
{ {
return m_jobCardCache; return util::Map<std::string, TrackedRecord<JobCard>>();
} }
/* /*
@@ -335,9 +284,22 @@ Parameters:
Returns: Returns:
- util::Map<std::string, TrackedRecord<Invoice>>: Collection of invoice records - util::Map<std::string, TrackedRecord<Invoice>>: Collection of invoice records
*/ */
util::Map<std::string, TrackedRecord<Invoice>>& DataStore::getInvoices() util::Map<std::string, TrackedRecord<Invoice>> DataStore::getInvoices()
{ {
return m_invoiceCache; return util::Map<std::string, TrackedRecord<Invoice>>();
}
/*
Function: getPayments
Description: Retrieves all payment records from the datastore.
Parameters:
- None
Returns:
- util::Map<std::string, TrackedRecord<Payment>>: Collection of payment records
*/
util::Map<std::string, TrackedRecord<Payment>> DataStore::getPayments()
{
return util::Map<std::string, TrackedRecord<Payment>>();
} }
/* /*
@@ -346,11 +308,11 @@ Description: Retrieves all service management observer records from the datastor
Parameters: Parameters:
- None - None
Returns: Returns:
- util::Map<std::string, User*>: Collection of observer records - util::Map<std::string, TrackedRecord<std::string>>: Collection of observer records
*/ */
util::Map<std::string, User*> DataStore::getServiceManagementObservers() util::Map<std::string, TrackedRecord<std::string>> DataStore::getServiceManagementObservers()
{ {
return util::Map<std::string, User*>(); return util::Map<std::string, TrackedRecord<std::string>>();
} }
/* /*
@@ -359,11 +321,11 @@ Description: Retrieves all payment management observer records from the datastor
Parameters: Parameters:
- None - None
Returns: Returns:
- util::Map<std::string, User*>: Collection of observer records - util::Map<std::string, TrackedRecord<std::string>>: Collection of observer records
*/ */
util::Map<std::string, User*> DataStore::getPaymentManagementObservers() util::Map<std::string, TrackedRecord<std::string>> DataStore::getPaymentManagementObservers()
{ {
return util::Map<std::string, User*>(); return util::Map<std::string, TrackedRecord<std::string>>();
} }
/* /*
@@ -372,49 +334,46 @@ Description: Retrieves all inventory management observer records from the datast
Parameters: Parameters:
- None - None
Returns: Returns:
- util::Map<std::string, User*>: Collection of observer records - util::Map<std::string, TrackedRecord<std::string>>: Collection of observer records
*/ */
util::Map<std::string, User*> DataStore::getInventoryManagementObservers() util::Map<std::string, TrackedRecord<std::string>> DataStore::getInventoryManagementObservers()
{ {
return util::Map<std::string, User*>(); return util::Map<std::string, TrackedRecord<std::string>>();
} }
/* /*
Function: saveUsers Function: saveUsers
Description: Persists all user records to the datastore. Description: Persists all user records to the datastore.
Parameters: Parameters:
- None - users: util::Map<std::string, TrackedRecord<User>>&, collection of user records
Returns: Returns:
- None - None
*/ */
void DataStore::saveUsers() void DataStore::saveUsers(util::Map<std::string, TrackedRecord<User>>& users)
{ {
saveRecords<User, SerializedUser>(m_users, m_userCache);
saveNotifications();
} }
/* /*
Function: saveNotifications Function: saveNotifications
Description: Persists all notification records to the datastore. Description: Persists all notification records to the datastore.
Parameters: Parameters:
- None - notifications: util::Map<std::string, TrackedRecord<Notification>>&, collection of notification records
Returns: Returns:
- None - None
*/ */
void DataStore::saveNotifications() void DataStore::saveNotifications(util::Map<std::string, TrackedRecord<Notification>>& notifications)
{ {
saveRecords<Notification, SerializedNotification>(m_notifications, m_notificationCache);
} }
/* /*
Function: saveServices Function: saveServices
Description: Persists all service records to the datastore. Description: Persists all service records to the datastore.
Parameters: Parameters:
- None - services: util::Map<std::string, TrackedRecord<Service>>&, collection of service records
Returns: Returns:
- None - None
*/ */
void DataStore::saveServices() void DataStore::saveServices(util::Map<std::string, TrackedRecord<Service>>& services)
{ {
} }
@@ -422,11 +381,11 @@ void DataStore::saveServices()
Function: saveComboPackages Function: saveComboPackages
Description: Persists all combo package records to the datastore. Description: Persists all combo package records to the datastore.
Parameters: Parameters:
- None - comboPackages: util::Map<std::string, TrackedRecord<ComboPackage>>&, collection of combo package records
Returns: Returns:
- None - None
*/ */
void DataStore::saveComboPackages() void DataStore::saveComboPackages(util::Map<std::string, TrackedRecord<ComboPackage>>& comboPackages)
{ {
} }
@@ -434,11 +393,11 @@ void DataStore::saveComboPackages()
Function: saveInventoryItems Function: saveInventoryItems
Description: Persists all inventory item records to the datastore. Description: Persists all inventory item records to the datastore.
Parameters: Parameters:
- None - inventoryItems: util::Map<std::string, TrackedRecord<InventoryItem>>&, collection of inventory item records
Returns: Returns:
- None - None
*/ */
void DataStore::saveInventoryItems() void DataStore::saveInventoryItems(util::Map<std::string, TrackedRecord<InventoryItem>>& inventoryItems)
{ {
} }
@@ -446,11 +405,11 @@ void DataStore::saveInventoryItems()
Function: saveServiceBookings Function: saveServiceBookings
Description: Persists all service booking records to the datastore. Description: Persists all service booking records to the datastore.
Parameters: Parameters:
- None - bookings: util::Map<std::string, TrackedRecord<ServiceBooking>>&, collection of service booking records
Returns: Returns:
- None - None
*/ */
void DataStore::saveServiceBookings() void DataStore::saveServiceBookings(util::Map<std::string, TrackedRecord<ServiceBooking>>& bookings)
{ {
} }
@@ -458,11 +417,11 @@ void DataStore::saveServiceBookings()
Function: saveJobCards Function: saveJobCards
Description: Persists all job card records to the datastore. Description: Persists all job card records to the datastore.
Parameters: Parameters:
- None - jobCards: util::Map<std::string, TrackedRecord<JobCard>>&, collection of job card records
Returns: Returns:
- None - None
*/ */
void DataStore::saveJobCards() void DataStore::saveJobCards(util::Map<std::string, TrackedRecord<JobCard>>& jobCards)
{ {
} }
@@ -470,11 +429,23 @@ void DataStore::saveJobCards()
Function: saveInvoices Function: saveInvoices
Description: Persists all invoice records to the datastore. Description: Persists all invoice records to the datastore.
Parameters: Parameters:
- None - invoices: util::Map<std::string, TrackedRecord<Invoice>>&, collection of invoice records
Returns: Returns:
- None - None
*/ */
void DataStore::saveInvoices() void DataStore::saveInvoices(util::Map<std::string, TrackedRecord<Invoice>>& invoices)
{
}
/*
Function: savePayments
Description: Persists all payment records to the datastore.
Parameters:
- payments: util::Map<std::string, TrackedRecord<Payment>>&, collection of payment records
Returns:
- None
*/
void DataStore::savePayments(util::Map<std::string, TrackedRecord<Payment>>& payments)
{ {
} }
@@ -486,7 +457,7 @@ Parameters:
Returns: Returns:
- None - None
*/ */
void DataStore::saveServiceManagementObservers(util::Map<std::string, User*>& observers) void DataStore::saveServiceManagementObservers(util::Map<std::string, TrackedRecord<std::string>>& observers)
{ {
} }
@@ -494,11 +465,11 @@ void DataStore::saveServiceManagementObservers(util::Map<std::string, User*>& ob
Function: savePaymentManagementObservers Function: savePaymentManagementObservers
Description: Persists all payment management observer records to the datastore. Description: Persists all payment management observer records to the datastore.
Parameters: Parameters:
- observers: util::Map<std::string, User*>&, collection of observer records - observers: util::Map<std::string, TrackedRecord<std::string>>&, collection of observer records
Returns: Returns:
- None - None
*/ */
void DataStore::savePaymentManagementObservers(util::Map<std::string, User*>& observers) void DataStore::savePaymentManagementObservers(util::Map<std::string, TrackedRecord<std::string>>& observers)
{ {
} }
@@ -506,11 +477,11 @@ void DataStore::savePaymentManagementObservers(util::Map<std::string, User*>& ob
Function: saveInventoryManagementObservers Function: saveInventoryManagementObservers
Description: Persists all inventory management observer records to the datastore. Description: Persists all inventory management observer records to the datastore.
Parameters: Parameters:
- observers: util::Map<std::string, User*>&, collection of observer records - observers: util::Map<std::string, TrackedRecord<std::string>>&, collection of observer records
Returns: Returns:
- None - None
*/ */
void DataStore::saveInventoryManagementObservers(util::Map<std::string, User*>& observers) void DataStore::saveInventoryManagementObservers(util::Map<std::string, TrackedRecord<std::string>>& observers)
{ {
} }
@@ -583,4 +554,3 @@ bool DataStore::unlockDataStore()
} }
return ReleaseMutex(m_globalMutex) != 0; return ReleaseMutex(m_globalMutex) != 0;
} }
@@ -20,12 +20,12 @@ class InventoryItem;
class ServiceBooking; class ServiceBooking;
class JobCard; class JobCard;
class Invoice; class Invoice;
class Payment;
class DataStore class DataStore
{ {
private: private:
DataStore(); DataStore();
~DataStore();
DataStore(const DataStore&) = delete; DataStore(const DataStore&) = delete;
DataStore& operator=(const DataStore&) = delete; DataStore& operator=(const DataStore&) = delete;
DataStore(DataStore&&) = delete; DataStore(DataStore&&) = delete;
@@ -43,40 +43,34 @@ private:
MappingInfo m_serviceManagementObservers; MappingInfo m_serviceManagementObservers;
MappingInfo m_paymentManagementObservers; MappingInfo m_paymentManagementObservers;
MappingInfo m_inventoryManagementObservers; MappingInfo m_inventoryManagementObservers;
util::Map<std::string, TrackedRecord<User>> m_userCache;
util::Map<std::string, TrackedRecord<Notification>> m_notificationCache;
util::Map<std::string, TrackedRecord<Service>> m_serviceCache;
util::Map<std::string, TrackedRecord<ComboPackage>> m_comboPackageCache;
util::Map<std::string, TrackedRecord<InventoryItem>> m_inventoryItemCache;
util::Map<std::string, TrackedRecord<ServiceBooking>> m_serviceBookingCache;
util::Map<std::string, TrackedRecord<JobCard>> m_jobCardCache;
util::Map<std::string, TrackedRecord<Invoice>> m_invoiceCache;
public: public:
static DataStore& getInstance(); static DataStore& getInstance();
bool initialize(); bool initialize();
void shutdown(); void shutdown();
util::Map<std::string, TrackedRecord<User>>& getUsers(); util::Map<std::string, TrackedRecord<User>> getUsers();
util::Map<std::string, TrackedRecord<Notification>>& getNotifications(); util::Map<std::string, TrackedRecord<Notification>> getNotifications();
util::Map<std::string, TrackedRecord<Service>>& getServices(); util::Map<std::string, TrackedRecord<Service>> getServices();
util::Map<std::string, TrackedRecord<ComboPackage>>& getComboPackages(); util::Map<std::string, TrackedRecord<ComboPackage>> getComboPackages();
util::Map<std::string, TrackedRecord<InventoryItem>>& getInventoryItems(); util::Map<std::string, TrackedRecord<InventoryItem>> getInventoryItems();
util::Map<std::string, TrackedRecord<ServiceBooking>>& getServiceBookings(); util::Map<std::string, TrackedRecord<ServiceBooking>> getServiceBookings();
util::Map<std::string, TrackedRecord<JobCard>>& getJobCards(); util::Map<std::string, TrackedRecord<JobCard>> getJobCards();
util::Map<std::string, TrackedRecord<Invoice>>& getInvoices(); util::Map<std::string, TrackedRecord<Invoice>> getInvoices();
util::Map<std::string, User*> getServiceManagementObservers(); util::Map<std::string, TrackedRecord<Payment>> getPayments();
util::Map<std::string, User*> getPaymentManagementObservers(); util::Map<std::string, TrackedRecord<std::string>> getServiceManagementObservers();
util::Map<std::string, User*> getInventoryManagementObservers(); util::Map<std::string, TrackedRecord<std::string>> getPaymentManagementObservers();
void saveUsers(); util::Map<std::string, TrackedRecord<std::string>> getInventoryManagementObservers();
void saveNotifications(); void saveUsers(util::Map<std::string, TrackedRecord<User>>& users);
void saveServices(); void saveNotifications(util::Map<std::string, TrackedRecord<Notification>>& notifications);
void saveComboPackages(); void saveServices(util::Map<std::string, TrackedRecord<Service>>& services);
void saveInventoryItems(); void saveComboPackages(util::Map<std::string, TrackedRecord<ComboPackage>>& comboPackages);
void saveServiceBookings(); void saveInventoryItems(util::Map<std::string, TrackedRecord<InventoryItem>>& inventoryItems);
void saveJobCards(); void saveServiceBookings(util::Map<std::string, TrackedRecord<ServiceBooking>>& bookings);
void saveInvoices(); void saveJobCards(util::Map<std::string, TrackedRecord<JobCard>>& jobCards);
void saveServiceManagementObservers(util::Map<std::string, User*>& observers); void saveInvoices(util::Map<std::string, TrackedRecord<Invoice>>& invoices);
void savePaymentManagementObservers(util::Map<std::string, User*>& observers); void savePayments(util::Map<std::string, TrackedRecord<Payment>>& payments);
void saveInventoryManagementObservers(util::Map<std::string, User*>& observers); void saveServiceManagementObservers(util::Map<std::string, TrackedRecord<std::string>>& observers);
void savePaymentManagementObservers(util::Map<std::string, TrackedRecord<std::string>>& observers);
void saveInventoryManagementObservers(util::Map<std::string, TrackedRecord<std::string>>& observers);
bool lockDataStore(); bool lockDataStore();
bool unlockDataStore(); bool unlockDataStore();
private: private:
@@ -84,8 +78,6 @@ private:
util::Map<std::string, TrackedRecord<TObject>> loadRecords(MappingInfo& mapping); util::Map<std::string, TrackedRecord<TObject>> loadRecords(MappingInfo& mapping);
template<typename TObject, typename TSerialized> template<typename TObject, typename TSerialized>
void saveRecords(MappingInfo& mapping, util::Map<std::string, TrackedRecord<TObject>>& records); void saveRecords(MappingInfo& mapping, util::Map<std::string, TrackedRecord<TObject>>& records);
template<typename TObject> void clearCache(util::Map<std::string, TrackedRecord<TObject>>&cache);
template<typename TObject> void refreshCache(util::Map<std::string, TrackedRecord<TObject>>&cache, util::Map<std::string, TrackedRecord<TObject>>&refreshedCache);
}; };
/* /*
@@ -131,7 +123,9 @@ Description: Persists all modified and newly added records
shared-memory mapping. Modified records overwrite shared-memory mapping. Modified records overwrite
their existing slots, while new records are their existing slots, while new records are
appended to the end of the mapping. Records marked appended to the end of the mapping. Records marked
as CLEAN are ignored. as CLEAN are ignored. After persistence, all
temporary objects owned by the tracked records are
destroyed to release memory.
Parameter: Parameter:
- mapping: Reference to the mapping where records are - mapping: Reference to the mapping where records are
stored. stored.
@@ -164,74 +158,14 @@ template<typename TObject, typename TSerialized> void DataStore::saveRecords(Map
continue; continue;
} }
size_t recordCount = SharedMemory::getRecordCount(mapping); size_t recordCount = SharedMemory::getRecordCount(mapping);
TSerialized* destination = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping,recordCount)); TSerialized* destination = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping, recordCount));
*destination = serialized; *destination = serialized;
SharedMemory::setRecordCount(mapping, recordCount + 1); 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<typename TObject>
void DataStore::clearCache(util::Map<std::string, TrackedRecord<TObject>>&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<typename TObject>
void DataStore::refreshCache(util::Map<std::string, TrackedRecord<TObject>>& cache, util::Map<std::string, TrackedRecord<TObject>>& refreshedCache)
{
util::Map<std::string, TrackedRecord<TObject>> oldCache = cache;
cache.clear();
for (int index = 0; index < refreshedCache.getSize(); ++index)
{
const std::string& id = refreshedCache.getKeyAt(index);
TrackedRecord<TObject>& refreshedRecord = refreshedCache.getValueAt(index);
int oldIndex = oldCache.find(id);
if (oldIndex != -1)
{
TrackedRecord<TObject>& oldRecord = oldCache.getValueAt(oldIndex);
*oldRecord.data = *refreshedRecord.data;
oldRecord.slotIndex = refreshedRecord.slotIndex;
oldRecord.state = refreshedRecord.state;
delete refreshedRecord.data;
refreshedRecord.data = oldRecord.data;
}
cache.insert(id, refreshedRecord);
}
for (int index = 0; index < oldCache.getSize(); ++index)
{
const std::string& id = oldCache.getKeyAt(index);
if (cache.find(id) == -1)
{
delete oldCache.getValueAt(index).data;
} }
} }
for (int index = 0; index < records.getSize(); ++index)
{
delete records.getValueAt(index).data;
records.getValueAt(index).data = nullptr;
}
} }
@@ -1,28 +0,0 @@
/*
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;
};
@@ -10,7 +10,6 @@ Created: 11-June-2026
*/ */
#include "SharedMemory.h" #include "SharedMemory.h"
#include "Windows.h"
#include "Config.h" #include "Config.h"
/* /*
@@ -20,9 +20,6 @@ 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
@@ -34,13 +31,12 @@ 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).data; User* user = usersMap.getValueAt(index);
if (user && user->getUserType() == util::UserType::ADMIN) if (user && user->getUserType() == util::UserType::ADMIN)
{ {
isAdminFound = true; isAdminFound = true;
@@ -77,9 +73,7 @@ void UserManagementService::createUser(const std::string& username, const std::s
InventoryManagementService inventoryManagementService; InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService; ServiceManagementService serviceManagementService;
DataStoreLockGuard lock(m_dataStore); auto& usersMap = m_dataStore.getUsers();
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");
@@ -93,14 +87,13 @@ 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);
trackedUsersMap.insert(newUser->getId(), util::createNewRecord(newUser)); usersMap.insert(newUser->getId(), 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();
} }
/* /*
@@ -114,24 +107,19 @@ 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)
{ {
DataStoreLockGuard lock(m_dataStore); auto& usersMap = m_dataStore.getUsers();
auto& trackedUsersMap = m_dataStore.getUsers(); int index = usersMap.find(userID);
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 = trackedUsersMap.getValueAt(index).data; User* user = usersMap.getValueAt(index);
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())
{ {
@@ -139,14 +127,9 @@ 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);
} }
/* /*
@@ -161,13 +144,12 @@ Throws:
*/ */
util::Vector<Notification*> UserManagementService::getUserNotifications(const std::string& userID) util::Vector<Notification*> UserManagementService::getUserNotifications(const std::string& userID)
{ {
DataStoreLockGuard lock(m_dataStore); auto& usersMap = m_dataStore.getUsers();
auto& trackedUsersMap = m_dataStore.getUsers(); if (usersMap.find(userID) == -1)
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 = trackedUsersMap[userID].data; User* user = usersMap[userID];
if (user) if (user)
{ {
auto& notifications = user->getNotifications(); auto& notifications = user->getNotifications();
@@ -187,41 +169,97 @@ util::Vector<Notification*> UserManagementService::getUserNotifications(const st
/* /*
Function: deleteNotification Function: deleteNotification
Description: Marks a specific notification associated with a given user Description: Deletes a specific notification associated with a given user ID.
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 - std::runtime_error if no user is found with the given UserID or if no notification is found with the given NotificationID.
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)
{ {
DataStoreLockGuard lock(m_dataStore); auto& usersMap = m_dataStore.getUsers();
auto& trackedUsersMap = m_dataStore.getUsers(); if (usersMap.find(userID) == -1)
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 = trackedUsersMap.getValueAt(userIndex).data; User* user = usersMap[userID];
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");
} }
int notificationIndex = trackedNotificationsMap.find(notificationID); notifications.remove(notificationID);
if (notificationIndex == -1) }
{
throw std::runtime_error("No notification found with given NotificationID"); /*
} Function: loadUsers
notifications[notificationID]->setState(util::State::INACTIVE); Description: Loads users and notifications from persistent storage into the datastore.
trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED; Validates that each notifications recipient exists and attaches the
m_dataStore.saveNotifications(); 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++)
{
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);
} }
/* /*
@@ -232,9 +270,7 @@ Return type: util::Map<std::string, User*>
*/ */
util::Map<std::string, User*> UserManagementService::getUsers() util::Map<std::string, User*> UserManagementService::getUsers()
{ {
DataStoreLockGuard lock(m_dataStore); return m_dataStore.getUsers();
auto users = util::getObjects(m_dataStore.getUsers());
return users;
} }
/* /*
@@ -245,12 +281,10 @@ Return type: User*
*/ */
User* UserManagementService::getUser(const std::string& userID) User* UserManagementService::getUser(const std::string& userID)
{ {
DataStoreLockGuard lock(m_dataStore); int index = m_dataStore.getUsers().find(userID);
auto& trackedUsersMap = m_dataStore.getUsers();
int index = trackedUsersMap.find(userID);
if (index != -1) if (index != -1)
{ {
return trackedUsersMap.getValueAt(index).data; return m_dataStore.getUsers().getValueAt(index);
} }
return nullptr; return nullptr;
} }
@@ -266,12 +300,10 @@ void UserManagementService::removeUser(const std::string& userID)
InventoryManagementService inventoryManagementService; InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService; ServiceManagementService serviceManagementService;
DataStoreLockGuard lock(m_dataStore); int index = m_dataStore.getUsers().find(userID);
auto& trackedUsersMap = m_dataStore.getUsers();
int index = trackedUsersMap.find(userID);
if (index != -1) if (index != -1)
{ {
User* user = trackedUsersMap.getValueAt(index).data; User* user = m_dataStore.getUsers().getValueAt(index);
if (user != nullptr) if (user != nullptr)
{ {
if (user->getUserType() == util::UserType::CUSTOMER) if (user->getUserType() == util::UserType::CUSTOMER)
@@ -282,37 +314,21 @@ 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)
{ {
DataStoreLockGuard lock(m_dataStore); util::Map<std::string, User*>& currentUsers = m_dataStore.getUsers();
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 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) if (currentUser && currentUser->getState() == util::State::ACTIVE && currentUser->getUserType() == type)
{ {
filteredUsersMap.insert(currentUser->getId(), currentUser); filteredUsersMap.insert(currentUser->getId(), currentUser);
@@ -31,4 +31,6 @@ 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();
}; };
@@ -99,80 +99,4 @@ namespace util
auto observerIDs = service->getObserverIDs(); auto observerIDs = service->getObserverIDs();
util::saveRecords(filePath, observerIDs); util::saveRecords(filePath, observerIDs);
} }
template<typename TObject>
Map<std::string, TObject*> getObjects(const Map<std::string, TrackedRecord<TObject>>& trackedRecords);
template<typename TObject>
Map<std::string, const TObject*> getConstObjects(const Map<std::string, TrackedRecord<TObject>>& trackedRecords);
template<typename TObject>
TrackedRecord<TObject> 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<std::string, TObject*>: Collection of object pointers.
*/
template<typename TObject>
util::Map<std::string, TObject*> util::getObjects(const util::Map<std::string, TrackedRecord<TObject>>& trackedRecords)
{
util::Map<std::string, TObject*> objects;
for (int index = 0; index < trackedRecords.getSize(); ++index)
{
const std::string& key = trackedRecords.getKeyAt(index);
TObject* object = trackedRecords.getValueAt(index).data;
objects.insert(key, object);
}
return objects;
}
/*
Function: getConstObjects
Description: Extracts the object pointers from a tracked-record
collection and returns them as a read-only map
keyed by the same identifiers.
Parameters:
- trackedRecords: Collection of tracked records.
Returns:
- Map<std::string, const TObject*>:
Collection of read-only object pointers.
*/
template<typename TObject>
util::Map<std::string, const TObject*> util::getConstObjects(
const util::Map<std::string, TrackedRecord<TObject>>& trackedRecords)
{
util::Map<std::string, const TObject*> 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<TObject>: Initialized tracked record.
*/
template<typename TObject>
TrackedRecord<TObject> util::createNewRecord(TObject* object)
{
TrackedRecord<TObject> record;
record.data = object;
record.state = RecordState::NEW_RECORD;
return record;
} }
@@ -27,11 +27,8 @@ void UserInterface::run()
{ {
try try
{ {
if (!m_controller.initialize()) m_controller.loadSystemData();
{ m_controller.runSystemChecks();
std::cout << "Error: Failed to initialize the system!";
return;
}
bool isMenuActive = true; bool isMenuActive = true;
while (isMenuActive) while (isMenuActive)
{ {
@@ -52,7 +49,7 @@ void UserInterface::run()
util::pressEnter(); util::pressEnter();
} }
} }
m_controller.shutdown(); m_controller.saveSystemData();
} }
catch (const std::invalid_argument& exception) catch (const std::invalid_argument& exception)
{ {