Files
Jissin Mathew f3220ee191 Implement Service Refactoring
<UserStory> 1927: Implement Service Refactorings </UserStory>

UserStory #1927

<Changes>
1. Added DataStoreLockGuard integration across ServiceManagementService methods for thread-safe datastore operations.
2. Updated DataStore::getServices(), getComboPackages(), getServiceBookings(), and getJobCards() to enrich records with linked entities (inventory items, services, bookings, technicians).
3. Modified ServiceManagementService::purchaseService() and purchaseComboPackage() to use tracked records and persist bookings safely.
4. Enhanced restoreInventory() and processBookingCancellation() to handle tracked records, update record states, and restore inventory items correctly.
5. Refactored cancelCustomerServiceBookings() and cancelTechnicianJobs() to use tracked records, restore inventory, and persist changes.
6. Updated createComboPackage(), removeComboPackage(), and createJobCard() to use tracked records and save changes to datastore.
7. Updated createService() to validate inventory items with tracked records and persist new services safely.
8. Added required header dependencies for DataStoreLockGuard and shared memory support.
</Changes>

<Test>
N/A
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
2026-06-15 10:47:06 +05:30

238 lines
9.1 KiB
C++

/*
File: DataStore.h
Description: Declares the DataStore singleton class responsible for managing collections of users, services, combo packages, service bookings, job cards, inventory items, invoices, and payments in the Vehicle Service Management System.
Author: Trenser
Date: 19-May-2026
*/
#pragma once
#include <windows.h>
#include <string>
#include "Map.h"
#include "MappingInfo.h"
#include "TrackedRecord.h"
#include "SerializedRecords.h"
#include "SharedMemory.h"
class User;
class Notification;
class Service;
class ComboPackage;
class InventoryItem;
class ServiceBooking;
class JobCard;
class Invoice;
class DataStore
{
private:
DataStore();
~DataStore();
DataStore(const DataStore&) = delete;
DataStore& operator=(const DataStore&) = delete;
DataStore(DataStore&&) = delete;
DataStore& operator=(DataStore&&) = delete;
HANDLE m_globalMutex;
MappingInfo m_users;
MappingInfo m_notifications;
MappingInfo m_services;
MappingInfo m_comboPackages;
MappingInfo m_inventoryItems;
MappingInfo m_serviceBookings;
MappingInfo m_jobCards;
MappingInfo m_invoices;
MappingInfo m_payments;
MappingInfo m_serviceManagementObservers;
MappingInfo m_paymentManagementObservers;
MappingInfo m_inventoryManagementObservers;
util::Map<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:
static DataStore& getInstance();
bool initialize();
void shutdown();
util::Map<std::string, TrackedRecord<User>>& getUsers();
util::Map<std::string, TrackedRecord<Notification>>& getNotifications();
util::Map<std::string, TrackedRecord<Service>>& getServices();
util::Map<std::string, TrackedRecord<ComboPackage>>& getComboPackages();
util::Map<std::string, TrackedRecord<InventoryItem>>& getInventoryItems();
util::Map<std::string, TrackedRecord<ServiceBooking>>& getServiceBookings();
util::Map<std::string, TrackedRecord<JobCard>>& getJobCards();
util::Map<std::string, TrackedRecord<Invoice>>& getInvoices();
util::Map<std::string, User*> getServiceManagementObservers();
util::Map<std::string, User*> getPaymentManagementObservers();
util::Map<std::string, User*> getInventoryManagementObservers();
void saveUsers();
void saveNotifications();
void saveServices();
void saveComboPackages();
void saveInventoryItems();
void saveServiceBookings();
void saveJobCards();
void saveInvoices();
void saveServiceManagementObservers(util::Map<std::string, User*>& observers);
void savePaymentManagementObservers(util::Map<std::string, User*>& observers);
void saveInventoryManagementObservers(util::Map<std::string, User*>& observers);
bool lockDataStore();
bool unlockDataStore();
private:
template<typename TObject, typename TSerialized>
util::Map<std::string, TrackedRecord<TObject>> loadRecords(MappingInfo& mapping);
template<typename TObject, typename TSerialized>
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);
};
/*
Function: loadRecords
Description: Loads all serialized records from the specified
shared-memory mapping, deserializes them into
application objects, and returns them as a map
of tracked records. Each loaded record is marked
as CLEAN and assigned its corresponding slot index
within the mapping so that future modifications
can be written back efficiently.
Parameter:
- mapping: Reference to the mapping containing the
serialized records.
Return type:
util::Map<std::string, TrackedRecord<TObject>>
A map containing all loaded records keyed by
their unique identifier.
*/
template<typename TObject, typename TSerialized>
util::Map<std::string, TrackedRecord<TObject>> DataStore::loadRecords(MappingInfo& mapping)
{
util::Map<std::string, TrackedRecord<TObject>> records;
SharedMemory::ensureLatestMapping(mapping);
size_t recordCount = SharedMemory::getRecordCount(mapping);
for (size_t index = 0; index < recordCount; ++index)
{
TSerialized* serialized = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping,index));
TObject* object = TObject::deserialize(*serialized);
TrackedRecord<TObject> trackedRecord;
trackedRecord.data = object;
trackedRecord.state = RecordState::CLEAN;
trackedRecord.slotIndex = index;
records.insert(object->getId(), trackedRecord);
}
return records;
}
/*
Function: saveRecords
Description: Persists all modified and newly added records
contained in the provided map to the specified
shared-memory mapping. Modified records overwrite
their existing slots, while new records are
appended to the end of the mapping. Records marked
as CLEAN are ignored.
Parameter:
- mapping: Reference to the mapping where records are
stored.
- records: Map containing the records to be persisted.
Return type: void
*/
template<typename TObject, typename TSerialized> void DataStore::saveRecords(MappingInfo& mapping, util::Map<std::string, TrackedRecord<TObject>>& records)
{
SharedMemory::ensureLatestMapping(mapping);
for (int index = 0; index < records.getSize(); ++index)
{
TrackedRecord<TObject>& record = records.getValueAt(index);
if (record.state == RecordState::CLEAN)
{
continue;
}
TSerialized serialized = record.data->serialize();
if (record.state == RecordState::MODIFIED)
{
TSerialized* destination = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping, record.slotIndex));
if (destination)
{
*destination = serialized;
}
}
else if (record.state == RecordState::NEW_RECORD)
{
if (!SharedMemory::ensureCapacityForInsert(mapping))
{
continue;
}
size_t recordCount = SharedMemory::getRecordCount(mapping);
TSerialized* destination = static_cast<TSerialized*>(SharedMemory::getRecordAddress(mapping,recordCount));
*destination = serialized;
SharedMemory::setRecordCount(mapping, recordCount + 1);
record.slotIndex = recordCount;
}
record.state = RecordState::CLEAN;
}
}
/*
Function: clearCache
Description: Releases all objects owned by the cache and
clears the cache contents.
Parameters:
- cache: Cache to be cleared.
Returns:
- None
*/
template<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;
}
}
}