Compare commits

..

3 Commits

Author SHA1 Message Date
joelthomastrenser 623cff9cfd changes 2026-06-12 18:59:51 +05:30
joelthomastrenser 3d884c8329 changes 2026-06-12 17:55:56 +05:30
joelthomastrenser 79d6b47aa8 Implement Notification Model Refactoring
<UserStory> 1953: Model Refactoring </UserStory>

UserStory #1953

<Changes>
1. Replaced CSV-based Notification serialization and deserialization with SerializedNotification record-based serialization for shared memory storage.
2. Implemented Notification::serialize() to convert Notification objects into fixed-size SerializedNotification structures.
3. Implemented Notification::deserialize() to reconstruct Notification objects directly from SerializedNotification records.
4. Added Notification state persistence by introducing util::State support in constructors, serialization, and deserialization flows.
5. Updated Notification class interfaces to use SerializedNotification types instead of std::string serialization APIs.
6. Removed legacy CSV serialization support, including CSV parsing logic and header generation functionality.
7. Added SerializedNotification dependencies through SerializedRecords.h inclusion and forward declaration support.
8. Initialized Notification objects with ACTIVE state by default and added state getter/setter APIs.
</Changes>

<Test>
N/A
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
2026-06-12 17:36:15 +05:30
31 changed files with 1344 additions and 654 deletions
+2 -2
View File
@@ -427,5 +427,5 @@ FodyWeavers.xsd
*.msm *.msm
*.msp *.msp
# DAT Files # CSV Files
*.dat *.csv
@@ -181,6 +181,7 @@
<ClInclude Include="utilities\Config.h" /> <ClInclude Include="utilities\Config.h" />
<ClInclude Include="utilities\Enums.h" /> <ClInclude Include="utilities\Enums.h" />
<ClInclude Include="utilities\FileHelper.h" /> <ClInclude Include="utilities\FileHelper.h" />
<ClInclude Include="utilities\FileManager.h" />
<ClInclude Include="utilities\InputHelper.h" /> <ClInclude Include="utilities\InputHelper.h" />
<ClInclude Include="utilities\Map.h" /> <ClInclude Include="utilities\Map.h" />
<ClInclude Include="utilities\OutputHelper.h" /> <ClInclude Include="utilities\OutputHelper.h" />
@@ -242,6 +242,9 @@
<ClInclude Include="utilities\Config.h"> <ClInclude Include="utilities\Config.h">
<Filter>Header Files\Utilities</Filter> <Filter>Header Files\Utilities</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="utilities\FileManager.h">
<Filter>Header Files\Utilities</Filter>
</ClInclude>
<ClInclude Include="utilities\StringHelper.h"> <ClInclude Include="utilities\StringHelper.h">
<Filter>Header Files\Utilities</Filter> <Filter>Header Files\Utilities</Filter>
</ClInclude> </ClInclude>
@@ -273,7 +276,7 @@
<Filter>Header Files\DataStores\SharedMemory</Filter> <Filter>Header Files\DataStores\SharedMemory</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="datastores\DataStoreLockGuard.h"> <ClInclude Include="datastores\DataStoreLockGuard.h">
<Filter>Header Files\DataStores</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -7,6 +7,8 @@ Date: 19-May-2026
#pragma once #pragma once
class Notification;
class Observer class Observer
{ {
public: public:
@@ -12,9 +12,6 @@ Date: 19-May-2026
#include "Config.h" #include "Config.h"
#include "SerializedRecords.h" #include "SerializedRecords.h"
#include "FileHelper.h" #include "FileHelper.h"
#include "ServiceBooking.h"
#include "JobCard.h"
#include "Invoice.h"
/* /*
Function: DataStore Function: DataStore
@@ -147,6 +144,11 @@ bool DataStore::initialize()
success = false; success = false;
break; break;
} }
if (!SharedMemory::createOrOpenMapping(m_payments))
{
success = false;
break;
}
if (!SharedMemory::createOrOpenMapping(m_serviceManagementObservers)) if (!SharedMemory::createOrOpenMapping(m_serviceManagementObservers))
{ {
success = false; success = false;
@@ -191,6 +193,7 @@ void DataStore::shutdown()
SharedMemory::closeMapping(m_serviceBookings); SharedMemory::closeMapping(m_serviceBookings);
SharedMemory::closeMapping(m_jobCards); SharedMemory::closeMapping(m_jobCards);
SharedMemory::closeMapping(m_invoices); SharedMemory::closeMapping(m_invoices);
SharedMemory::closeMapping(m_payments);
SharedMemory::closeMapping(m_serviceManagementObservers); SharedMemory::closeMapping(m_serviceManagementObservers);
SharedMemory::closeMapping(m_paymentManagementObservers); SharedMemory::closeMapping(m_paymentManagementObservers);
SharedMemory::closeMapping(m_inventoryManagementObservers); SharedMemory::closeMapping(m_inventoryManagementObservers);
@@ -255,28 +258,6 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<Service>>& DataStore::getServices() util::Map<std::string, TrackedRecord<Service>>& DataStore::getServices()
{ {
util::Map<std::string, TrackedRecord<Service>> services = loadRecords<Service, SerializedService>(m_services);
refreshCache(m_serviceCache, services);
util::Map<std::string, TrackedRecord<InventoryItem>>& inventoryItems = getInventoryItems();
size_t numberOfServices = m_serviceCache.getSize();
for (int iteratorOne =0; iteratorOne < numberOfServices; iteratorOne++)
{
Service* currentService = m_serviceCache.getValueAt(iteratorOne).data;
util::Map<std::string, InventoryItem*> inventoryItemMap;
util::Vector<std::string> currentServiceInventoryItem = currentService->getRequiredInventoryItemIDs();
for (int iteratorTwo = 0; iteratorTwo < currentServiceInventoryItem.getSize(); iteratorTwo++)
{
const std::string& currentInventoryItemId = currentServiceInventoryItem[iteratorTwo];
int currentInventoryItemIndex = inventoryItems.find(currentInventoryItemId);
if (currentInventoryItemIndex == -1)
{
throw std::runtime_error("Invalid inventory item ID");
}
InventoryItem* currentItem = inventoryItems.getValueAt(currentInventoryItemIndex).data;
inventoryItemMap[currentInventoryItemId] = currentItem;
}
currentService->setRequiredInventoryItems(inventoryItemMap);
}
return m_serviceCache; return m_serviceCache;
} }
@@ -290,28 +271,6 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<ComboPackage>>& DataStore::getComboPackages() util::Map<std::string, TrackedRecord<ComboPackage>>& DataStore::getComboPackages()
{ {
util::Map<std::string, TrackedRecord<ComboPackage>> comboPackages = loadRecords<ComboPackage, SerializedComboPackage>(m_comboPackages);
refreshCache(m_comboPackageCache, comboPackages);
util::Map<std::string, TrackedRecord<Service>>& services = getServices();
size_t numberOfComboPackages = m_comboPackageCache.getSize();
for (int iteratorOne = 0; iteratorOne < numberOfComboPackages; iteratorOne++)
{
ComboPackage* currentComboPackage = m_comboPackageCache.getValueAt(iteratorOne).data;
util::Vector<std::string> currentServiceIds = currentComboPackage->getServiceIDs();
util::Map<std::string, Service*> currentComboPackageServices;
for (int iteratorTwo = 0; iteratorTwo < currentServiceIds.getSize(); iteratorTwo++)
{
const std::string& currentServiceId = currentServiceIds[iteratorTwo];
int serviceIndex = services.find(currentServiceId);
if (serviceIndex == -1)
{
throw std::runtime_error("Invalid service ID");
}
Service* currentService = services.getValueAt(serviceIndex).data;
currentComboPackageServices[currentServiceId] = currentService;
}
currentComboPackage->setServices(currentComboPackageServices);
}
return m_comboPackageCache; return m_comboPackageCache;
} }
@@ -325,8 +284,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;
} }
@@ -340,49 +297,6 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBookings() util::Map<std::string, TrackedRecord<ServiceBooking>>& DataStore::getServiceBookings()
{ {
util::Map<std::string, TrackedRecord<ServiceBooking>> serviceBookings = loadRecords<ServiceBooking, SerializedServiceBooking>(m_serviceBookings);
refreshCache(m_serviceBookingCache, serviceBookings);
auto& users = getUsers();
auto& services = getServices();
size_t numberOfServiceBookings = m_serviceBookingCache.getSize();
for (int iteratorOne = 0; iteratorOne < numberOfServiceBookings; iteratorOne++)
{
ServiceBooking* serviceBooking = m_serviceBookingCache.getValueAt(iteratorOne).data;
auto& serviceIds = serviceBooking->getServiceIDs();
util::Map<std::string, Service*> servicesInBooking;
for (int iteratorTwo = 0; iteratorTwo < serviceIds.getSize(); iteratorTwo++)
{
const std::string& currentServiceId = serviceIds[iteratorTwo];
int serviceIndex = services.find(currentServiceId);
if (serviceIndex == -1)
{
throw std::runtime_error("Invalid service index.");
}
auto currentService = services.getValueAt(serviceIndex);
servicesInBooking[currentServiceId] = currentService.data;
}
serviceBooking->setServices(servicesInBooking);
if (!serviceBooking->getCustomerId().empty())
{
int userIndex = users.find(serviceBooking->getCustomerId());
if (userIndex == -1)
{
throw std::runtime_error("Invalid user index.");
}
auto customer = users.getValueAt(userIndex);
serviceBooking->setCustomer(customer.data);
}
if (!serviceBooking->getAssignedTechnicianId().empty())
{
int technicianIndex = users.find(serviceBooking->getAssignedTechnicianId());
if (technicianIndex == -1)
{
throw std::runtime_error("Invalid technician index.");
}
auto technician = users.getValueAt(technicianIndex);
serviceBooking->setAssignedTechnician(technician.data);
}
}
return m_serviceBookingCache; return m_serviceBookingCache;
} }
@@ -396,51 +310,9 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<JobCard>>& DataStore::getJobCards() util::Map<std::string, TrackedRecord<JobCard>>& DataStore::getJobCards()
{ {
util::Map<std::string, TrackedRecord<JobCard>> jobCards = loadRecords<JobCard, SerializedJobCard>(m_jobCards);
refreshCache(m_jobCardCache, jobCards);
auto& serviceBookings = getServiceBookings();
auto& services = getServices();
auto& users = getUsers();
int numberOfJobCards = m_jobCardCache.getSize();
for (int iterator = 0; iterator < numberOfJobCards; iterator++)
{
JobCard* jobCard = m_jobCardCache.getValueAt(iterator).data;
if (!jobCard)
{
continue;
}
const std::string& bookingId = jobCard->getBookingId();
int bookingIndex = serviceBookings.find(bookingId);
if (bookingIndex == -1)
{
throw std::runtime_error("Invalid booking ID: " + bookingId);
}
auto trackedBooking = serviceBookings.getValueAt(bookingIndex);
jobCard->setBooking(trackedBooking.data);
const std::string& serviceId = jobCard->getServiceId();
int serviceIndex = services.find(serviceId);
if (serviceIndex == -1)
{
throw std::runtime_error("Invalid service ID: " + serviceId);
}
auto trackedService = services.getValueAt(serviceIndex);
jobCard->setService(trackedService.data);
const std::string& technicianId = jobCard->getTechnicianId();
if (!technicianId.empty())
{
int technicianIndex = users.find(technicianId);
if (technicianIndex == -1)
{
throw std::runtime_error("Invalid technician ID: " + technicianId);
}
auto trackedTechnician = users.getValueAt(technicianIndex);
jobCard->setTechnician(trackedTechnician.data);
}
}
return m_jobCardCache; return m_jobCardCache;
} }
/* /*
Function: getInvoices Function: getInvoices
Description: Retrieves all invoice records from the datastore. Description: Retrieves all invoice records from the datastore.
@@ -451,45 +323,6 @@ Returns:
*/ */
util::Map<std::string, TrackedRecord<Invoice>>& DataStore::getInvoices() util::Map<std::string, TrackedRecord<Invoice>>& DataStore::getInvoices()
{ {
auto& serviceBookings = getServiceBookings();
auto& inventoryItems = getInventoryItems();
util::Map<std::string, TrackedRecord<Invoice>> invoices = loadRecords<Invoice, SerializedInvoice>(m_invoices);
refreshCache(m_invoiceCache, invoices);
for (int iterator = 0; iterator < m_invoiceCache.getSize(); iterator++)
{
auto& trackedInvoice = m_invoiceCache.getValueAt(iterator);
Invoice* invoice = trackedInvoice.data;
if (!invoice)
{
continue;
}
const std::string& currentBookingId = invoice->getBookingId();
int currentBookingIndex = serviceBookings.find(currentBookingId);
if (currentBookingIndex == -1)
{
throw std::runtime_error("Invalid Service Booking Index.");
}
ServiceBooking* currentBooking = serviceBookings.getValueAt(currentBookingIndex).data;
auto& currentInventoryItemIds = invoice->getPartIDs();
util::Map<std::string, InventoryItem*> currentInventoryItems;
for (int iterator = 0; iterator < currentInventoryItemIds.getSize(); iterator++)
{
const std::string& currentItemId = currentInventoryItemIds[iterator];
int currentItemIndex = inventoryItems.find(currentItemId);
if (currentItemIndex == -1)
{
throw std::runtime_error("Invalid Inventory item id.");
}
InventoryItem* currentItem = inventoryItems.getValueAt(currentItemIndex).data;
if (!currentItem)
{
continue;
}
currentInventoryItems[currentItemId] = currentItem;
}
invoice->setBooking(currentBooking);
invoice->setParts(currentInventoryItems);
}
return m_invoiceCache; return m_invoiceCache;
} }
@@ -599,7 +432,6 @@ Returns:
*/ */
void DataStore::saveServices() void DataStore::saveServices()
{ {
saveRecords<Service, SerializedService>(m_services, m_serviceCache);
} }
/* /*
@@ -612,7 +444,6 @@ Returns:
*/ */
void DataStore::saveComboPackages() void DataStore::saveComboPackages()
{ {
saveRecords<ComboPackage, SerializedComboPackage>(m_comboPackages, m_comboPackageCache);
} }
/* /*
@@ -625,7 +456,6 @@ Returns:
*/ */
void DataStore::saveInventoryItems() void DataStore::saveInventoryItems()
{ {
saveRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems, m_inventoryItemCache);
} }
/* /*
@@ -638,7 +468,6 @@ Returns:
*/ */
void DataStore::saveServiceBookings() void DataStore::saveServiceBookings()
{ {
saveRecords<ServiceBooking, SerializedServiceBooking>(m_serviceBookings, m_serviceBookingCache);
} }
/* /*
@@ -651,7 +480,6 @@ Returns:
*/ */
void DataStore::saveJobCards() void DataStore::saveJobCards()
{ {
saveRecords<JobCard, SerializedJobCard>(m_jobCards, m_jobCardCache);
} }
/* /*
@@ -664,7 +492,6 @@ Returns:
*/ */
void DataStore::saveInvoices() void DataStore::saveInvoices()
{ {
saveRecords<Invoice, SerializedInvoice>(m_invoices, m_invoiceCache);
} }
/* /*
@@ -693,7 +520,7 @@ void DataStore::saveObservers(MappingInfo& mapping, util::Map<std::string, User*
{ {
SerializedObserver serializedObserver; SerializedObserver serializedObserver;
User* user = observers.getValueAt(static_cast<int>(index)); User* user = observers.getValueAt(static_cast<int>(index));
strcpy_s(serializedObserver.id, sizeof(serializedObserver.id), user->getId().c_str()); strcpy_s(serializedObserver.id, user->getId().c_str());
SerializedObserver* destination = static_cast<SerializedObserver*>(SharedMemory::getRecordAddress(mapping, index)); SerializedObserver* destination = static_cast<SerializedObserver*>(SharedMemory::getRecordAddress(mapping, index));
*destination = serializedObserver; *destination = serializedObserver;
} }
@@ -738,6 +565,32 @@ void DataStore::saveInventoryManagementObservers(util::Map<std::string, User*>&
saveObservers(m_inventoryManagementObservers, observers); saveObservers(m_inventoryManagementObservers, observers);
} }
/*
Function: lockDataStore
Description: Acquires exclusive access to the datastore.
Parameters:
- None
Returns:
- bool: True if the datastore was successfully locked, otherwise false
*/
bool DataStore::lockDataStore()
{
return false;
}
/*
Function: unlockDataStore
Description: Releases exclusive access to the datastore.
Parameters:
- None
Returns:
- bool: True if the datastore was successfully unlocked, otherwise false
*/
bool DataStore::unlockDataStore()
{
return false;
}
/* /*
Function: lockDataStore Function: lockDataStore
Description: Acquires the datastore mutex, providing Description: Acquires the datastore mutex, providing
@@ -781,3 +634,4 @@ bool DataStore::unlockDataStore()
} }
return ReleaseMutex(m_globalMutex) != 0; return ReleaseMutex(m_globalMutex) != 0;
} }
@@ -11,16 +11,15 @@ Date: 19-May-2026
#include "Map.h" #include "Map.h"
#include "MappingInfo.h" #include "MappingInfo.h"
#include "TrackedRecord.h" #include "TrackedRecord.h"
#include "SerializedRecords.h"
#include "SharedMemory.h" #include "SharedMemory.h"
#include "User.h" class User;
#include "Notification.h" class Notification;
#include "Service.h" class Service;
#include "ComboPackage.h" class ComboPackage;
#include "InventoryItem.h" class InventoryItem;
#include "ServiceBooking.h" class ServiceBooking;
#include "JobCard.h" class JobCard;
#include "Invoice.h" class Invoice;
class DataStore class DataStore
{ {
@@ -40,6 +39,7 @@ private:
MappingInfo m_serviceBookings; MappingInfo m_serviceBookings;
MappingInfo m_jobCards; MappingInfo m_jobCards;
MappingInfo m_invoices; MappingInfo m_invoices;
MappingInfo m_payments;
MappingInfo m_serviceManagementObservers; MappingInfo m_serviceManagementObservers;
MappingInfo m_paymentManagementObservers; MappingInfo m_paymentManagementObservers;
MappingInfo m_inventoryManagementObservers; MappingInfo m_inventoryManagementObservers;
@@ -231,17 +231,9 @@ void DataStore::refreshCache(util::Map<std::string, TrackedRecord<TObject>>& cac
for (int index = 0; index < oldCache.getSize(); ++index) for (int index = 0; index < oldCache.getSize(); ++index)
{ {
const std::string& id = oldCache.getKeyAt(index); const std::string& id = oldCache.getKeyAt(index);
const TrackedRecord<TObject>& localTrackedRecord = oldCache.getValueAt(index);
if (cache.find(id) == -1) if (cache.find(id) == -1)
{ {
if (localTrackedRecord.state == RecordState::NEW_RECORD) delete oldCache.getValueAt(index).data;
{
cache.insert(id, localTrackedRecord);
}
else
{
delete localTrackedRecord.data;
}
} }
} }
} }
@@ -9,7 +9,6 @@ Date: 19-May-2026
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "SerializedRecords.h"
#include "ComboPackage.h" #include "ComboPackage.h"
#include "Service.h" #include "Service.h"
#include "Factory.h" #include "Factory.h"
@@ -29,8 +28,7 @@ Returns:
ComboPackage::ComboPackage() ComboPackage::ComboPackage()
: m_id("CMP" + std::to_string(++m_uid)), : m_id("CMP" + std::to_string(++m_uid)),
m_status(util::State::ACTIVE), m_status(util::State::ACTIVE),
m_discountPercentage(0.0) { m_discountPercentage(0.0) {}
}
/* /*
Function: ComboPackage Function: ComboPackage
@@ -272,38 +270,72 @@ static util::Vector<std::string> getServiceIDsAsVector(const std::string& servic
/* /*
Function: serialize Function: serialize
Description: Serializes the ComboPackage object into a SerializedComboPackage record. Description: Serializes the combo package into a CSV-formatted string.
Parameters: Parameters:
- None - None
Returns: Returns:
- SerializedComboPackage: Serialized representation of the combo package - std::string: Serialized combo package record
*/ */
SerializedComboPackage ComboPackage::serialize() const std::string ComboPackage::serialize() const
{ {
SerializedComboPackage serialized = {}; std::ostringstream serializedComboPackage;
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); serializedComboPackage << m_id << ','
strcpy_s(serialized.packageName, sizeof(serialized.packageName), m_packageName.c_str()); << m_packageName << ','
strcpy_s(serialized.serviceIDs, sizeof(serialized.serviceIDs), getServiceIDsAsString(m_serviceIDs).c_str()); << m_discountPercentage << ','
serialized.discountPercentage = m_discountPercentage; << getServiceIDsAsString(m_serviceIDs) << ','
serialized.status = m_status; << util::getStateString(m_status);
return serialized; return serializedComboPackage.str();
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a SerializedComboPackage record into a ComboPackage object. Description: Deserializes a CSV-formatted string into a ComboPackage object.
Parameters: Parameters:
- serializedComboPackage: const SerializedComboPackage&, serialized combo package record - record: const std::string&, serialized combo package record
Returns: Returns:
- ComboPackage*: Pointer to the deserialized ComboPackage object - ComboPackage*: Pointer to the deserialized ComboPackage object
Throws:
- std::runtime_error if data is invalid
*/ */
ComboPackage* ComboPackage::deserialize(const SerializedComboPackage& serializedComboPackage) ComboPackage* ComboPackage::deserialize(const std::string& record)
{ {
util::Vector<std::string> serviceIDs = getServiceIDsAsVector(serializedComboPackage.serviceIDs); std::string id, packageName;
std::string discountPercentageString, serviceIDsString, statusString;
double discountPercentage;
std::istringstream serializedComboPackage(record);
getline(serializedComboPackage, id, ',');
getline(serializedComboPackage, packageName, ',');
getline(serializedComboPackage, discountPercentageString, ',');
getline(serializedComboPackage, serviceIDsString, ',');
getline(serializedComboPackage, statusString, ',');
try
{
discountPercentage = std::stod(discountPercentageString);
}
catch (...)
{
throw std::runtime_error("Invalid combo package data");
}
util::Vector<std::string> serviceIDs = getServiceIDsAsVector(serviceIDsString);
util::State status = util::getState(statusString);
return Factory::getObject<ComboPackage>( return Factory::getObject<ComboPackage>(
serializedComboPackage.id, id,
serializedComboPackage.packageName, packageName,
serializedComboPackage.discountPercentage, discountPercentage,
serviceIDs, serviceIDs,
serializedComboPackage.status); status
);
}
/*
Function: getHeaders
Description: Retrieves the CSV headers for combo package serialization.
Parameters:
- None
Returns:
- std::string: Header string ("ID,PackageName,DiscountPercentage,ServiceIDs,Status")
*/
std::string ComboPackage::getHeaders()
{
return "ID,PackageName,DiscountPercentage,ServiceIDs,Status";
} }
@@ -12,7 +12,6 @@ Date: 19-May-2026
#include "Enums.h" #include "Enums.h"
class Service; class Service;
struct SerializedComboPackage;
class ComboPackage class ComboPackage
{ {
@@ -39,6 +38,7 @@ public:
void setDiscountPercentage(double discountPercentage); void setDiscountPercentage(double discountPercentage);
void setServices(const util::Map<std::string, Service*>& services); void setServices(const util::Map<std::string, Service*>& services);
void setState(util::State status); void setState(util::State status);
SerializedComboPackage serialize() const; std::string serialize() const;
static ComboPackage* deserialize(const SerializedComboPackage&); static ComboPackage* deserialize(const std::string&);
static std::string getHeaders();
}; };
@@ -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();
}; };
@@ -9,7 +9,6 @@ Date: 19-May-2026
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "SerializedRecords.h"
#include "Invoice.h" #include "Invoice.h"
#include "Factory.h" #include "Factory.h"
#include "InventoryItem.h" #include "InventoryItem.h"
@@ -35,8 +34,7 @@ Invoice::Invoice()
m_discountPercentage(0.0), m_discountPercentage(0.0),
m_totalAmount(0.0), m_totalAmount(0.0),
m_paymentMethod(util::PaymentMode()), m_paymentMethod(util::PaymentMode()),
m_status(util::PaymentStatus()) { m_status(util::PaymentStatus()) {}
}
/* /*
Function: Invoice Function: Invoice
@@ -68,7 +66,7 @@ Invoice::Invoice(
const util::Timestamp& paymentDate, const util::Timestamp& paymentDate,
util::PaymentMode paymentMethod, util::PaymentMode paymentMethod,
util::PaymentStatus status util::PaymentStatus status
) )
: m_id("INV" + std::to_string(++m_uid)), : m_id("INV" + std::to_string(++m_uid)),
m_bookingId(bookingId), m_bookingId(bookingId),
m_booking(booking), m_booking(booking),
@@ -475,50 +473,100 @@ static util::Vector<std::string> getPartIDsAsVector(const std::string& partIDsSt
/* /*
Function: serialize Function: serialize
Description: Serializes the Invoice object into a SerializedInvoice record. Description: Serializes the invoice into a CSV-formatted string.
Parameters: Parameters:
- None - None
Returns: Returns:
- SerializedInvoice: Serialized representation of the invoice - std::string: Serialized invoice record
*/ */
SerializedInvoice Invoice::serialize() const std::string Invoice::serialize() const
{ {
SerializedInvoice serialized = {}; std::ostringstream serializedInvoice;
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); serializedInvoice << m_id << ','
strcpy_s(serialized.bookingId, sizeof(serialized.bookingId), m_bookingId.c_str()); << m_bookingId << ','
strcpy_s(serialized.partIDs, sizeof(serialized.partIDs), getPartIDsAsString(m_partIDs).c_str()); << m_invoiceDate.toString() << ','
serialized.invoiceDate = m_invoiceDate; << m_laborCost << ','
serialized.laborCost = m_laborCost; << getPartIDsAsString(m_partIDs) << ','
serialized.partsCost = m_partsCost; << m_partsCost << ','
serialized.discountPercentage = m_discountPercentage; << m_discountPercentage << ','
serialized.totalAmount = m_totalAmount; << m_totalAmount << ','
serialized.paymentDate = m_paymentDate; << m_paymentDate.toString() << ','
serialized.paymentMethod = m_paymentMethod; << util::getPaymentModeString(m_paymentMethod) << ','
serialized.status = m_status; << util::getPaymentStatusString(m_status);
return serialized; return serializedInvoice.str();
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a SerializedInvoice record into an Invoice object. Description: Deserializes a CSV-formatted string into an Invoice object.
Parameters: Parameters:
- serializedInvoice: const SerializedInvoice&, serialized invoice record - record: const std::string&, serialized invoice record
Returns: Returns:
- Invoice*: Pointer to the deserialized Invoice object - Invoice*: Pointer to the deserialized Invoice object
Throws:
- std::runtime_error if data is invalid
*/ */
Invoice* Invoice::deserialize(const SerializedInvoice& serializedInvoice) Invoice* Invoice::deserialize(const std::string& record)
{ {
util::Vector<std::string> partIDs = getPartIDsAsVector(serializedInvoice.partIDs); std::string id, bookingId;
std::string invoiceDateString, laborCostString, partIDsString;
std::string partsCostString, discountPercentageString, totalAmountString;
std::string paymentDateString, paymentMethodString, statusString;
double laborCost, partsCost, discountPercentage, totalAmount;
std::istringstream serializedInvoice(record);
getline(serializedInvoice, id, ',');
getline(serializedInvoice, bookingId, ',');
getline(serializedInvoice, invoiceDateString, ',');
getline(serializedInvoice, laborCostString, ',');
getline(serializedInvoice, partIDsString, ',');
getline(serializedInvoice, partsCostString, ',');
getline(serializedInvoice, discountPercentageString, ',');
getline(serializedInvoice, totalAmountString, ',');
getline(serializedInvoice, paymentDateString, ',');
getline(serializedInvoice, paymentMethodString, ',');
getline(serializedInvoice, statusString, ',');
util::Timestamp invoiceDate;
util::Timestamp paymentDate;
try
{
invoiceDate = util::Timestamp::fromString(invoiceDateString);
paymentDate = util::Timestamp::fromString(paymentDateString);
laborCost = std::stod(laborCostString);
partsCost = std::stod(partsCostString);
discountPercentage = std::stod(discountPercentageString);
totalAmount = std::stod(totalAmountString);
}
catch (...)
{
throw std::runtime_error("Invalid invoice data");
}
util::Vector<std::string> partIDs = getPartIDsAsVector(partIDsString);
util::PaymentMode paymentMethod = util::getPaymentMode(paymentMethodString);
util::PaymentStatus status = util::getPaymentStatus(statusString);
return Factory::getObject<Invoice>( return Factory::getObject<Invoice>(
serializedInvoice.id, id,
serializedInvoice.bookingId, bookingId,
serializedInvoice.invoiceDate, invoiceDate,
partIDs, partIDs,
serializedInvoice.laborCost, laborCost,
serializedInvoice.partsCost, partsCost,
serializedInvoice.discountPercentage, discountPercentage,
serializedInvoice.totalAmount, totalAmount,
serializedInvoice.paymentDate, paymentDate,
serializedInvoice.paymentMethod, paymentMethod,
serializedInvoice.status); status
);
}
/*
Function: getHeaders
Description: Retrieves the CSV headers for invoice serialization.
Parameters:
- None
Returns:
- std::string: Header string ("ID,BookingID,InvoiceDate,LaborCost,PartIDs,PartsCost,DiscountPercentage,TotalAmount,PaymentDate,PaymentMethod,Status")
*/
std::string Invoice::getHeaders()
{
return "ID,BookingID,InvoiceDate,LaborCost,PartIDs,PartsCost,DiscountPercentage,TotalAmount,PaymentDate,PaymentMethod,Status";
} }
@@ -6,6 +6,7 @@ Author: Trenser
Date: 19-May-2026 Date: 19-May-2026
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include "Map.h" #include "Map.h"
@@ -15,7 +16,6 @@ Date: 19-May-2026
class ServiceBooking; class ServiceBooking;
class InventoryItem; class InventoryItem;
struct SerializedInvoice;
class Invoice class Invoice
{ {
@@ -41,7 +41,7 @@ public:
ServiceBooking* booking, ServiceBooking* booking,
const util::Timestamp& invoiceDate, const util::Timestamp& invoiceDate,
double laborCost, double laborCost,
const util::Map<std::string, InventoryItem*>& parts, const util::Map<std::string,InventoryItem*>& parts,
double partsCost, double partsCost,
double discountPercentage, double discountPercentage,
double totalAmount, double totalAmount,
@@ -87,6 +87,7 @@ public:
void setPaymentDate(const util::Timestamp& paymentDate); void setPaymentDate(const util::Timestamp& paymentDate);
void setPaymentMethod(util::PaymentMode paymentMethod); void setPaymentMethod(util::PaymentMode paymentMethod);
void setStatus(util::PaymentStatus status); void setStatus(util::PaymentStatus status);
SerializedInvoice serialize() const; std::string serialize() const;
static Invoice* deserialize(const SerializedInvoice&); static Invoice* deserialize(const std::string&);
static std::string getHeaders();
}; };
@@ -9,7 +9,6 @@ Date:19-May-2026
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "SerializedRecords.h"
#include "JobCard.h" #include "JobCard.h"
#include "Factory.h" #include "Factory.h"
#include "StringHelper.h" #include "StringHelper.h"
@@ -29,8 +28,7 @@ JobCard::JobCard()
m_booking(nullptr), m_booking(nullptr),
m_service(nullptr), m_service(nullptr),
m_technician(nullptr), m_technician(nullptr),
m_status(util::ServiceJobStatus()) { m_status(util::ServiceJobStatus()) {}
}
/* /*
Function: JobCard Function: JobCard
@@ -67,8 +65,7 @@ JobCard::JobCard(const std::string& bookingId,
m_technician(technician), m_technician(technician),
m_assignedDate(assignedDate), m_assignedDate(assignedDate),
m_status(status), m_status(status),
m_completionDate(completionDate) { m_completionDate(completionDate) {}
}
/* /*
Function: JobCard (parameterized constructor with ID) Function: JobCard (parameterized constructor with ID)
@@ -354,41 +351,79 @@ void JobCard::setCompletionDate(const util::Timestamp& completionDate)
/* /*
Function: serialize Function: serialize
Description: Serializes the JobCard object into a SerializedJobCard record. Description: Serializes the job card into a CSV-formatted string.
Parameters: Parameters:
- None - None
Returns: Returns:
- SerializedJobCard: Serialized representation of the job card - std::string: Serialized job card record
*/ */
SerializedJobCard JobCard::serialize() const std::string JobCard::serialize() const
{ {
SerializedJobCard serialized = {}; std::ostringstream serializedJobCard;
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); serializedJobCard << m_id << ','
strcpy_s(serialized.bookingId, sizeof(serialized.bookingId), m_bookingId.c_str()); << m_bookingId << ','
strcpy_s(serialized.serviceId, sizeof(serialized.serviceId), m_serviceId.c_str()); << m_serviceId << ','
strcpy_s(serialized.technicianId, sizeof(serialized.technicianId), m_technicianId.c_str()); << m_technicianId << ','
serialized.assignedDate = m_assignedDate; << m_assignedDate.toString() << ','
serialized.status = m_status; << util::getServiceJobStatusString(m_status) << ','
serialized.completionDate = m_completionDate; << m_completionDate.toString();
return serialized; return serializedJobCard.str();
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a SerializedJobCard record into a JobCard object. Description: Deserializes a CSV-formatted string into a JobCard object.
Parameters: Parameters:
- serializedJobCard: const SerializedJobCard&, serialized job card record - record: const std::string&, serialized job card record
Returns: Returns:
- JobCard*: Pointer to the deserialized JobCard object - JobCard*: Pointer to the deserialized JobCard object
Throws:
- std::runtime_error if timestamp parsing fails
*/ */
JobCard* JobCard::deserialize(const SerializedJobCard& serializedJobCard) JobCard* JobCard::deserialize(const std::string& record)
{ {
std::string id, bookingId, serviceId, technicianId;
std::string assignedDateString, statusString, completionDateString;
std::istringstream serializedJobCard(record);
getline(serializedJobCard, id, ',');
getline(serializedJobCard, bookingId, ',');
getline(serializedJobCard, serviceId, ',');
getline(serializedJobCard, technicianId, ',');
getline(serializedJobCard, assignedDateString, ',');
getline(serializedJobCard, statusString, ',');
getline(serializedJobCard, completionDateString, ',');
util::Timestamp assignedDate;
util::Timestamp completionDate;
try
{
assignedDate = util::Timestamp::fromString(assignedDateString);
completionDate = util::Timestamp::fromString(completionDateString);
}
catch (...)
{
throw std::runtime_error("Invalid timestamp");
}
util::ServiceJobStatus status = util::getServiceJobStatus(statusString);
return Factory::getObject<JobCard>( return Factory::getObject<JobCard>(
serializedJobCard.id, id,
serializedJobCard.bookingId, bookingId,
serializedJobCard.serviceId, serviceId,
serializedJobCard.technicianId, technicianId,
serializedJobCard.assignedDate, assignedDate,
serializedJobCard.status, status,
serializedJobCard.completionDate); completionDate
);
}
/*
Function: getHeaders
Description: Retrieves the CSV headers for job card serialization.
Parameters:
- None
Returns:
- std::string: Header string ("ID,BookingID,ServiceID,TechnicianID,AssignedDate,Status,CompletionDate")
*/
std::string JobCard::getHeaders()
{
return "ID,BookingID,ServiceID,TechnicianID,AssignedDate,Status,CompletionDate";
} }
@@ -15,7 +15,6 @@ Date:19-May-2026
class ServiceBooking; class ServiceBooking;
class Service; class Service;
class User; class User;
struct SerializedJobCard;
class JobCard class JobCard
{ {
@@ -71,6 +70,7 @@ public:
void setAssignedDate(const util::Timestamp& assignedDate); void setAssignedDate(const util::Timestamp& assignedDate);
void setStatus(util::ServiceJobStatus status); void setStatus(util::ServiceJobStatus status);
void setCompletionDate(const util::Timestamp& completionDate); void setCompletionDate(const util::Timestamp& completionDate);
SerializedJobCard serialize() const; std::string serialize() const;
static JobCard* deserialize(const SerializedJobCard&); static JobCard* deserialize(const std::string&);
static std::string getHeaders();
}; };
@@ -8,7 +8,6 @@ Date: 19-May-2026
*/ */
#include <sstream> #include <sstream>
#include "SerializedRecords.h"
#include "Service.h" #include "Service.h"
#include "InventoryItem.h" #include "InventoryItem.h"
#include "StringHelper.h" #include "StringHelper.h"
@@ -28,8 +27,7 @@ Returns:
Service::Service() Service::Service()
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SRV" + std::to_string(++m_uid)),
m_status(util::State::ACTIVE), m_status(util::State::ACTIVE),
m_laborCost(0.0) { m_laborCost(0.0) {}
}
/* /*
Function: Service Function: Service
@@ -268,38 +266,72 @@ static util::Vector<std::string> getInventoryItemIDsAsVector(const std::string&
/* /*
Function: serialize Function: serialize
Description: Serializes the Service object into a SerializedService record. Description: Serializes the service into a CSV-formatted string.
Parameters: Parameters:
- None - None
Returns: Returns:
- SerializedService: Serialized representation of the service - std::string: Serialized service record
*/ */
SerializedService Service::serialize() const std::string Service::serialize() const
{ {
SerializedService serialized = {}; std::ostringstream serializedService;
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); serializedService << m_id << ','
strcpy_s(serialized.name, sizeof(serialized.name), m_name.c_str()); << m_name << ','
strcpy_s(serialized.inventoryItemIDs, sizeof(serialized.inventoryItemIDs), getInventoryItemIDsAsString(m_requiredInventoryItemIDs).c_str()); << getInventoryItemIDsAsString(m_requiredInventoryItemIDs) << ','
serialized.laborCost = m_laborCost; << m_laborCost << ','
serialized.status = m_status; << util::getStateString(m_status);
return serialized; return serializedService.str();
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a SerializedService record into a Service object. Description: Deserializes a CSV-formatted string into a Service object.
Parameters: Parameters:
- serializedService: const SerializedService&, serialized service record - record: const std::string&, serialized service record
Returns: Returns:
- Service*: Pointer to the deserialized Service object - Service*: Pointer to the deserialized Service object
Throws:
- std::runtime_error if labor cost parsing fails
*/ */
Service* Service::deserialize(const SerializedService& serializedService) Service* Service::deserialize(const std::string& record)
{ {
util::Vector<std::string> inventoryItemIDs = getInventoryItemIDsAsVector(serializedService.inventoryItemIDs); std::string id, name;
std::string inventoryItemIDsString, laborCostString, statusString;
double laborCost;
std::istringstream serializedService(record);
getline(serializedService, id, ',');
getline(serializedService, name, ',');
getline(serializedService, inventoryItemIDsString, ',');
getline(serializedService, laborCostString, ',');
getline(serializedService, statusString, ',');
util::Vector<std::string> inventoryItemIDs = getInventoryItemIDsAsVector(inventoryItemIDsString);
try
{
laborCost = std::stod(laborCostString);
}
catch (...)
{
throw std::runtime_error("Invalid labor cost");
}
util::State status = util::getState(statusString);
return Factory::getObject<Service>( return Factory::getObject<Service>(
serializedService.id, id,
serializedService.name, name,
inventoryItemIDs, inventoryItemIDs,
serializedService.laborCost, laborCost,
serializedService.status); status
);
}
/*
Function: getHeaders
Description: Retrieves the CSV headers for service serialization.
Parameters:
- None
Returns:
- std::string: Header string ("ID,Name,InventoryIDs,LaborCost,Status")
*/
std::string Service::getHeaders()
{
return "ID,Name,InventoryIDs,LaborCost,Status";
} }
@@ -6,6 +6,7 @@ Author: Trenser
Date: 19-May-2026 Date: 19-May-2026
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include "Map.h" #include "Map.h"
@@ -13,7 +14,6 @@ Date: 19-May-2026
#include "Enums.h" #include "Enums.h"
class InventoryItem; class InventoryItem;
struct SerializedService;
class Service class Service
{ {
@@ -40,6 +40,7 @@ public:
void setRequiredInventoryItems(const util::Map<std::string, InventoryItem*>& requiredInventoryItems); void setRequiredInventoryItems(const util::Map<std::string, InventoryItem*>& requiredInventoryItems);
void setLaborCost(double laborCost); void setLaborCost(double laborCost);
void setState(util::State status); void setState(util::State status);
SerializedService serialize() const; std::string serialize() const;
static Service* deserialize(const SerializedService&); static Service* deserialize(const std::string&);
static std::string getHeaders();
}; };
@@ -6,10 +6,8 @@ Description: Implementation file containing the method definitions of the
Author: Trenser Author: Trenser
Date:19-May-2026 Date:19-May-2026
*/ */
#include <stdexcept> #include <stdexcept>
#include <sstream> #include <sstream>
#include "SerializedRecords.h"
#include "ServiceBooking.h" #include "ServiceBooking.h"
#include "Service.h" #include "Service.h"
#include "Enums.h" #include "Enums.h"
@@ -30,8 +28,7 @@ ServiceBooking::ServiceBooking()
m_customer(nullptr), m_customer(nullptr),
m_assignedTechnician(nullptr), m_assignedTechnician(nullptr),
m_status(util::ServiceJobStatus::PENDING), m_status(util::ServiceJobStatus::PENDING),
m_discountPercentage(0.0) { m_discountPercentage(0.0) {}
}
/* /*
Function: ServiceBooking Function: ServiceBooking
@@ -440,46 +437,84 @@ static util::Vector<std::string> getServiceIDsAsVector(const std::string& servic
/* /*
Function: serialize Function: serialize
Description: Serializes the ServiceBooking object into a SerializedServiceBooking record. Description: Serializes the service booking into a CSV-formatted string.
Parameters: Parameters:
- None - None
Returns: Returns:
- SerializedServiceBooking: Serialized representation of the service booking - std::string: Serialized booking record
*/ */
SerializedServiceBooking ServiceBooking::serialize() const std::string ServiceBooking::serialize() const
{ {
SerializedServiceBooking serialized = {}; std::ostringstream serializedBooking;
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str()); serializedBooking << m_id << ','
strcpy_s(serialized.serviceIDs, sizeof(serialized.serviceIDs), getServiceIDsAsString(m_serviceIDs).c_str()); << util::getServiceJobStatusString(m_status) << ','
strcpy_s(serialized.customerId, sizeof(serialized.customerId), m_customerId.c_str()); << getServiceIDsAsString(m_serviceIDs) << ','
strcpy_s(serialized.vehicleNumber, sizeof(serialized.vehicleNumber), m_vehicleNumber.c_str()); << m_customerId << ','
strcpy_s(serialized.vehicleBrand, sizeof(serialized.vehicleBrand), m_vehicleBrand.c_str()); << m_vehicleNumber << ','
strcpy_s(serialized.vehicleModel, sizeof(serialized.vehicleModel), m_vehicleModel.c_str()); << m_vehicleBrand << ','
strcpy_s(serialized.assignedTechnicianId, sizeof(serialized.assignedTechnicianId), m_assignedTechnicianId.c_str()); << m_vehicleModel << ','
serialized.status = m_status; << m_assignedTechnicianId << ','
serialized.discountPercentage = m_discountPercentage; << m_discountPercentage << ',';
return serialized; return serializedBooking.str();
} }
/* /*
Function: deserialize Function: deserialize
Description: Deserializes a SerializedServiceBooking record into a ServiceBooking object. Description: Deserializes a CSV-formatted string into a ServiceBooking object.
Parameters: Parameters:
- serializedServiceBooking: const SerializedServiceBooking&, serialized service booking record - record: const std::string&, serialized booking record
Returns: Returns:
- ServiceBooking*: Pointer to the deserialized ServiceBooking object - ServiceBooking*: Pointer to the deserialized ServiceBooking object
Throws:
- std::runtime_error if discount percentage parsing fails
*/ */
ServiceBooking* ServiceBooking::deserialize(const SerializedServiceBooking& serializedServiceBooking) ServiceBooking* ServiceBooking::deserialize(const std::string& record)
{ {
util::Vector<std::string> serviceIDs = getServiceIDsAsVector(serializedServiceBooking.serviceIDs); std::string id, customerId, vehicleNumber, vehicleBrand, vehicleModel, assignedTechnicianId;
std::string serviceJobStatusString, serviceIDsString, discountPercentageString;
double discountPercentage;
std::istringstream serializedBooking(record);
getline(serializedBooking, id, ',');
getline(serializedBooking, serviceJobStatusString, ',');
getline(serializedBooking, serviceIDsString, ',');
getline(serializedBooking, customerId, ',');
getline(serializedBooking, vehicleNumber, ',');
getline(serializedBooking, vehicleBrand, ',');
getline(serializedBooking, vehicleModel, ',');
getline(serializedBooking, assignedTechnicianId, ',');
getline(serializedBooking, discountPercentageString, ',');
util::Vector<std::string> serviceIDs = getServiceIDsAsVector(serviceIDsString);
try
{
discountPercentage = std::stod(discountPercentageString);
}
catch (...)
{
throw std::runtime_error("Invalid discount percentage");
}
util::ServiceJobStatus status = util::getServiceJobStatus(serviceJobStatusString);
return Factory::getObject<ServiceBooking>( return Factory::getObject<ServiceBooking>(
serializedServiceBooking.id, id,
serializedServiceBooking.status, status,
serviceIDs, serviceIDs,
serializedServiceBooking.customerId, customerId,
serializedServiceBooking.vehicleNumber, vehicleNumber,
serializedServiceBooking.vehicleBrand, vehicleBrand,
serializedServiceBooking.vehicleModel, vehicleModel,
serializedServiceBooking.assignedTechnicianId, assignedTechnicianId,
serializedServiceBooking.discountPercentage); discountPercentage
);
}
/*
Function: getHeaders
Description: Retrieves the CSV headers for service booking serialization.
Parameters:
- None
Returns:
- std::string: Header string ("ID,Status,ServiceIDs,CustomerID,VehicleNumber,VehicleBrand,VehicleModel,AssignedTechnicianID,DiscountPercentage")
*/
std::string ServiceBooking::getHeaders()
{
return "ID,Status,ServiceIDs,CustomerID,VehicleNumber,VehicleBrand,VehicleModel,AssignedTechnicianID,DiscountPercentage";
} }
@@ -6,7 +6,6 @@ Description: Header file declaring the ServiceBooking class, which represents
Author: Trenser Author: Trenser
Date:19-May-2026 Date:19-May-2026
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include "Map.h" #include "Map.h"
@@ -15,7 +14,6 @@ Date:19-May-2026
class Service; class Service;
class User; class User;
struct SerializedServiceBooking;
class ServiceBooking class ServiceBooking
{ {
@@ -80,6 +78,7 @@ public:
void setAssignedTechnicianId(const std::string& assignedTechnicianId); void setAssignedTechnicianId(const std::string& assignedTechnicianId);
void setAssignedTechnician(User* assignedTechnician); void setAssignedTechnician(User* assignedTechnician);
void setDiscountPercentage(double discountPercentage); void setDiscountPercentage(double discountPercentage);
SerializedServiceBooking serialize() const; std::string serialize() const;
static ServiceBooking* deserialize(const SerializedServiceBooking&); static ServiceBooking* deserialize(const std::string&);
static std::string getHeaders();
}; };
@@ -10,8 +10,6 @@ Date:19-May-2026
#include <stdexcept> #include <stdexcept>
#include "AuthenticationManagementService.h" #include "AuthenticationManagementService.h"
#include "User.h" #include "User.h"
#include "Utility.h"
#include "DataStoreLockGuard.h"
User* AuthenticationManagementService::m_authenticatedUser = nullptr; User* AuthenticationManagementService::m_authenticatedUser = nullptr;
@@ -26,12 +24,11 @@ Return type: bool - true if login successful, false otherwise
*/ */
bool AuthenticationManagementService::login(const std::string& username, const std::string& password) bool AuthenticationManagementService::login(const std::string& username, const std::string& password)
{ {
DataStoreLockGuard lock(m_dataStore); util::Map<std::string, User*> users = m_dataStore.getUsers();
auto& trackedUserMap = m_dataStore.getUsers(); int usersMapSize = users.getSize();
int trackedUserMapSize = trackedUserMap.getSize(); for (int index = 0; index < usersMapSize; index++)
for (int index = 0; index < trackedUserMapSize; index++)
{ {
User* user = trackedUserMap.getValueAt(index).data; User* user = users.getValueAt(index);
if (username == user->getUserName()) if (username == user->getUserName())
{ {
if (password == user->getPassword()) if (password == user->getPassword())
@@ -77,18 +74,9 @@ Return type: void
*/ */
void AuthenticationManagementService::changePassword(const std::string& newPassword) void AuthenticationManagementService::changePassword(const std::string& newPassword)
{ {
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsersMap = m_dataStore.getUsers();
if (m_authenticatedUser == nullptr) if (m_authenticatedUser == nullptr)
{ {
throw std::runtime_error("There is no user currently logged in!"); throw std::runtime_error("There is no user currently logged in!");
} }
int index = trackedUsersMap.find(m_authenticatedUser->getId());
if (index == -1)
{
throw std::runtime_error("User does not exist!\n");
}
m_authenticatedUser->setPassword(newPassword); m_authenticatedUser->setPassword(newPassword);
trackedUsersMap.getValueAt(index).state = RecordState::MODIFIED;
m_dataStore.saveUsers();
} }
@@ -12,6 +12,7 @@ Date: 22-May-2026
#include "Config.h" #include "Config.h"
#include "Enums.h" #include "Enums.h"
#include "Factory.h" #include "Factory.h"
#include "FileManager.h"
#include "InventoryItem.h" #include "InventoryItem.h"
#include "InventoryManagementService.h" #include "InventoryManagementService.h"
#include "Timestamp.h" #include "Timestamp.h"
@@ -57,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);
@@ -82,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);
@@ -90,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
@@ -101,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();
} }
/* /*
@@ -117,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();
} }
/* /*
@@ -143,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;
} }
/* /*
@@ -157,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();
} }
/* /*
@@ -182,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;
} }
/* /*
@@ -21,6 +21,7 @@ class InventoryManagementService : public NotificationManagementService
private: private:
DataStore& m_dataStore; DataStore& m_dataStore;
static util::Map<std::string, User*> m_observers; static util::Map<std::string, User*> m_observers;
util::Vector<std::string> getObserverIDs() override;
public: public:
InventoryManagementService() : m_dataStore(DataStore::getInstance()) {} InventoryManagementService() : m_dataStore(DataStore::getInstance()) {}
util::Map<std::string, InventoryItem*> getInventoryItems(); util::Map<std::string, InventoryItem*> getInventoryItems();
@@ -32,4 +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 saveObservers();
}; };
@@ -19,4 +19,5 @@ public:
virtual void sendNotification(User* recipient, const std::string& title, const std::string& message) = 0; virtual void sendNotification(User* recipient, const std::string& title, const std::string& message) = 0;
virtual void attach(User* user) = 0; virtual void attach(User* user) = 0;
virtual void detach(User* user) = 0; virtual void detach(User* user) = 0;
virtual util::Vector<std::string> getObserverIDs() = 0;
}; };
@@ -11,11 +11,11 @@ Date: 20-May-2026
#include "Config.h" #include "Config.h"
#include "Enums.h" #include "Enums.h"
#include "Factory.h" #include "Factory.h"
#include "FileManager.h"
#include "InventoryItem.h" #include "InventoryItem.h"
#include "Invoice.h" #include "Invoice.h"
#include "JobCard.h" #include "JobCard.h"
#include "PaymentManagementService.h" #include "PaymentManagementService.h"
#include "DataStoreLockGuard.h"
#include "Service.h" #include "Service.h"
#include "ServiceBooking.h" #include "ServiceBooking.h"
#include "Timestamp.h" #include "Timestamp.h"
@@ -122,12 +122,11 @@ Returns:
*/ */
void PaymentManagementService::sendPaymentReminders() void PaymentManagementService::sendPaymentReminders()
{ {
DataStoreLockGuard lock(m_dataStore); auto& invoicesMap = m_dataStore.getInvoices();
auto& trackedInvoicesMap = m_dataStore.getInvoices(); int invoicesMapSize = invoicesMap.getSize();
int invoicesMapSize = trackedInvoicesMap.getSize();
for (int index = 0; index < invoicesMapSize; index++) for (int index = 0; index < invoicesMapSize; index++)
{ {
const Invoice* invoice = trackedInvoicesMap.getValueAt(index).data; const Invoice* invoice = invoicesMap.getValueAt(index);
if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING) if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING)
{ {
util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate(); util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate();
@@ -150,6 +149,123 @@ void PaymentManagementService::sendPaymentReminders()
} }
} }
/*
Function: getObserverIDs
Description: Retrieves the IDs of all observers currently attached to the
PaymentManagementService.
Parameters:
- None
Returns:
- util::Vector<std::string>: Vector of observer user IDs
*/
util::Vector<std::string> PaymentManagementService::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: loadInvoices
Description: Loads invoices from persistent storage into the datastore.
Validates associated service bookings and inventory parts before
attaching them to each invoice. Throws exceptions if invalid IDs
are encountered.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if a booking ID or part ID is invalid
*/
void PaymentManagementService::loadInvoices()
{
util::FileManager<Invoice> invoiceFileManager(config::file::INVOICE_FILE);
auto& invoices = m_dataStore.getInvoices();
auto& serviceBookings = m_dataStore.getServiceBookings();
auto& inventoryItems = m_dataStore.getInventoryItems();
auto invoicesMap = invoiceFileManager.load();
for (int invoiceIndex = 0; invoiceIndex < invoicesMap.getSize(); invoiceIndex++)
{
Invoice* invoice = invoicesMap.getValueAt(invoiceIndex);
int bookingIndex = serviceBookings.find(invoice->getBookingId());
if (bookingIndex == -1)
{
throw std::runtime_error("Invalid Booking ID");
}
ServiceBooking* booking = serviceBookings.getValueAt(bookingIndex);
invoice->setBooking(booking);
util::Map<std::string, InventoryItem*> invoiceParts;
auto& partIDs = invoice->getPartIDs();
for (int partIndex = 0; partIndex < partIDs.getSize(); partIndex++)
{
const std::string& partID = partIDs[partIndex];
int inventoryIndex = inventoryItems.find(partID);
if (inventoryIndex == -1)
{
throw std::runtime_error("Invalid Part ID");
}
invoiceParts[partID] = inventoryItems.getValueAt(inventoryIndex);
}
invoice->setParts(invoiceParts);
invoices[invoice->getId()] = invoice;
}
}
/*
Function: saveInvoices
Description: Saves invoices from the datastore to persistent storage.
Uses FileManager to serialize invoices into the configured file.
Parameters:
- None
Returns:
- void
*/
void PaymentManagementService::saveInvoices()
{
util::FileManager<Invoice> invoiceFileManager(config::file::INVOICE_FILE);
auto& invoices = m_dataStore.getInvoices();
invoiceFileManager.save(invoices);
}
/*
Function: loadObservers
Description: Loads observer IDs from persistent storage and attaches corresponding
users as observers to the PaymentManagementService.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if an observer ID is invalid (not found in datastore)
*/
void PaymentManagementService::loadObservers()
{
util::loadObservers(config::file::PAYMENTMANAGEMENTOBSERVERS, this, m_dataStore);
}
/*
Function: saveObservers
Description: Saves the current observer IDs of the PaymentManagementService
to persistent storage for future retrieval.
Parameters:
- None
Returns:
- void
*/
void PaymentManagementService::saveObservers()
{
util::saveObservers(config::file::PAYMENTMANAGEMENTOBSERVERS, this);
}
/* /*
Function: createInventoryItemsMap (static helper) Function: createInventoryItemsMap (static helper)
Description: Builds a map of inventory items required for a given service and adds them to the bookings inventory map. Description: Builds a map of inventory items required for a given service and adds them to the bookings inventory map.
@@ -183,7 +299,6 @@ Throws:
*/ */
void PaymentManagementService::generateInvoice(ServiceBooking* booking) void PaymentManagementService::generateInvoice(ServiceBooking* booking)
{ {
DataStoreLockGuard lock(m_dataStore);
if (!booking) if (!booking)
{ {
throw std::runtime_error("Invoice generation failed: booking is null."); throw std::runtime_error("Invoice generation failed: booking is null.");
@@ -193,10 +308,10 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
std::string bookingID = booking->getId(); std::string bookingID = booking->getId();
util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices(); util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices();
util::Map<std::string, InventoryItem*> completeInventoryItemMapOfBooking; util::Map<std::string, InventoryItem*> completeInventoryItemMapOfBooking;
auto& currentTrackedJobCards = m_dataStore.getJobCards(); util::Map<std::string, JobCard*> currentJobCards = m_dataStore.getJobCards();
for (int iterator = 0; iterator < currentTrackedJobCards.getSize(); iterator++) for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++)
{ {
JobCard* currentJobCard = currentTrackedJobCards.getValueAt(iterator).data; JobCard* currentJobCard = currentJobCards.getValueAt(iterator);
util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus(); util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus();
if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED) if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED)
{ {
@@ -216,9 +331,8 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
totalServiceCost = totalLaborCost + totalPartsCost; totalServiceCost = totalLaborCost + totalPartsCost;
totalServiceCost -= (totalServiceCost * (discountPercentage / 100)); totalServiceCost -= (totalServiceCost * (discountPercentage / 100));
Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLaborCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING); Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLaborCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING);
auto& currentTrackedInvoices = m_dataStore.getInvoices(); util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices();
currentTrackedInvoices.insert(invoice->getId(), util::createNewRecord(invoice)); currentInvoices.insert(invoice->getId(), invoice);
m_dataStore.saveInvoices();
} }
/* /*
@@ -231,12 +345,11 @@ Returns:
*/ */
util::Map<std::string, Invoice*> PaymentManagementService::getInvoices(const std::string& customerID) util::Map<std::string, Invoice*> PaymentManagementService::getInvoices(const std::string& customerID)
{ {
DataStoreLockGuard lock(m_dataStore); util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices();
auto& currentTrackedInvoices = m_dataStore.getInvoices();
util::Map<std::string, Invoice*> currentUserInvoices; util::Map<std::string, Invoice*> currentUserInvoices;
for (int iterator = 0; iterator < currentTrackedInvoices.getSize(); iterator++) for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++)
{ {
Invoice* currentInvoice = currentTrackedInvoices.getValueAt(iterator).data; Invoice* currentInvoice = currentInvoices.getValueAt(iterator);
if (currentInvoice->getBooking()->getCustomerId() == customerID) if (currentInvoice->getBooking()->getCustomerId() == customerID)
{ {
currentUserInvoices.insert(currentInvoice->getId(), currentInvoice); currentUserInvoices.insert(currentInvoice->getId(), currentInvoice);
@@ -259,13 +372,11 @@ Throws:
*/ */
void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode) void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode)
{ {
DataStoreLockGuard lock(m_dataStore); auto& currentInvoices = m_dataStore.getInvoices();
auto& currentTrackedInvoices = m_dataStore.getInvoices(); int invoiceIndex = currentInvoices.find(invoiceID);
int invoiceIndex = currentTrackedInvoices.find(invoiceID);
if (invoiceIndex != -1) if (invoiceIndex != -1)
{ {
auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex); Invoice* invoice = currentInvoices.getValueAt(invoiceIndex);
Invoice* invoice = trackedInvoice.data;
if (invoice && invoice->getStatus() != util::PaymentStatus::PAID) if (invoice && invoice->getStatus() != util::PaymentStatus::PAID)
{ {
User* currentUser = invoice->getBooking()->getCustomer(); User* currentUser = invoice->getBooking()->getCustomer();
@@ -276,14 +387,12 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
title = "Payment successful"; title = "Payment successful";
message = "Payment successful for Invoice ID " + invoiceID; message = "Payment successful for Invoice ID " + invoiceID;
sendNotification(currentUser, title, message); sendNotification(currentUser, title, message);
trackedInvoice.state = RecordState::MODIFIED;
} }
} }
else else
{ {
throw std::runtime_error("Payment failed: invalid invoice ID."); throw std::runtime_error("Payment failed: invalid invoice ID.");
} }
m_dataStore.saveInvoices();
} }
/* /*
@@ -292,14 +401,11 @@ Description: Provides access to all invoices stored in the data store.
Parameters: Parameters:
- none - none
Returns: Returns:
- util::Map<std::string, Invoice*>: Map of invoice IDs to invoice objects - util::Map<std::string, Invoice*>&: Map of invoice IDs to invoice objects
*/ */
util::Map<std::string, Invoice*> PaymentManagementService::getAllInvoices() util::Map<std::string, Invoice*>& PaymentManagementService::getAllInvoices()
{ {
DataStoreLockGuard lock(m_dataStore); return m_dataStore.getInvoices();
util::Map<std::string, Invoice*> invoices;
invoices = util::getObjects(m_dataStore.getInvoices());
return invoices;
} }
/* /*
@@ -315,24 +421,20 @@ Throws:
*/ */
void PaymentManagementService::confirmPayment(const std::string& invoiceID) void PaymentManagementService::confirmPayment(const std::string& invoiceID)
{ {
DataStoreLockGuard lock(m_dataStore); auto& currentInvoices = m_dataStore.getInvoices();
auto& currentTrackedInvoices = m_dataStore.getInvoices(); int invoiceIndex = currentInvoices.find(invoiceID);
int invoiceIndex = currentTrackedInvoices.find(invoiceID);
if (invoiceIndex == -1) if (invoiceIndex == -1)
{ {
throw std::runtime_error("Payment confirmation failed: invalid invoice ID."); throw std::runtime_error("Payment confirmation failed: invalid invoice ID.");
} }
auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex); Invoice* invoice = currentInvoices.getValueAt(invoiceIndex);
Invoice* invoice = trackedInvoice.data;
if (!invoice || invoice->getStatus() != util::PaymentStatus::PAID) if (!invoice || invoice->getStatus() != util::PaymentStatus::PAID)
{ {
throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation."); throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation.");
} }
User* currentUser = invoice->getBooking()->getCustomer(); User* currentUser = invoice->getBooking()->getCustomer();
invoice->setStatus(util::PaymentStatus::COMPLETED); invoice->setStatus(util::PaymentStatus::COMPLETED);
trackedInvoice.state = RecordState::MODIFIED;
std::string title = "Payment Confirmed"; std::string title = "Payment Confirmed";
std::string message = "Payment Confirmed for Invoice ID " + invoiceID; std::string message = "Payment Confirmed for Invoice ID " + invoiceID;
sendNotification(currentUser, title, message); sendNotification(currentUser, title, message);
m_dataStore.saveInvoices();
} }
@@ -22,15 +22,20 @@ class PaymentManagementService : public NotificationManagementService
private: private:
DataStore& m_dataStore; DataStore& m_dataStore;
static util::Map<std::string, User*> m_observers; static util::Map<std::string, User*> m_observers;
util::Vector<std::string> getObserverIDs() override;
public: public:
PaymentManagementService() : m_dataStore(DataStore::getInstance()) {} PaymentManagementService() : m_dataStore(DataStore::getInstance()) {}
void generateInvoice(ServiceBooking* booking); void generateInvoice(ServiceBooking* booking);
util::Map<std::string, Invoice*> getInvoices(const std::string& customerID); util::Map<std::string, Invoice*> getInvoices(const std::string& customerID);
void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode); void completePayment(const std::string& invoiceID, util::PaymentMode paymentMode);
util::Map<std::string, Invoice*> getAllInvoices(); util::Map<std::string, Invoice*>& getAllInvoices();
void confirmPayment(const std::string& invoiceID); void confirmPayment(const std::string& invoiceID);
void sendPaymentReminders(); void sendPaymentReminders();
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 loadInvoices();
void saveInvoices();
void loadObservers();
void saveObservers();
}; };
@@ -14,6 +14,7 @@ Date:19-May-2026
#include "DataStore.h" #include "DataStore.h"
#include "Enums.h" #include "Enums.h"
#include "Factory.h" #include "Factory.h"
#include "FileManager.h"
#include "InventoryItem.h" #include "InventoryItem.h"
#include "JobCard.h" #include "JobCard.h"
#include "NotificationManagementService.h" #include "NotificationManagementService.h"
@@ -24,7 +25,6 @@ Date:19-May-2026
#include "Timestamp.h" #include "Timestamp.h"
#include "User.h" #include "User.h"
#include "UserManagementService.h" #include "UserManagementService.h"
#include "DataStoreLockGuard.h"
#include "Utility.h" #include "Utility.h"
#include "DataStoreLockGuard.h" #include "DataStoreLockGuard.h"
@@ -47,19 +47,18 @@ void ServiceManagementService::purchaseService(const util::Vector<std::string>&
{ {
throw std::runtime_error("No user is currently logged in!"); throw std::runtime_error("No user is currently logged in!");
} }
DataStoreLockGuard lock(m_dataStore); auto& servicesMap = m_dataStore.getServices();
auto& trackedServicesMap = m_dataStore.getServices(); auto& serviceBookingMap = m_dataStore.getServiceBookings();
auto& trackedServiceBookingMap = m_dataStore.getServiceBookings();
util::Map<std::string, Service*> selectedServices; util::Map<std::string, Service*> selectedServices;
int selectedServicesCount = serviceIDs.getSize(); int selectedServicesCount = serviceIDs.getSize();
for (int index = 0; index < selectedServicesCount; index++) for (int index = 0; index < selectedServicesCount; index++)
{ {
int serviceIndex = trackedServicesMap.find(serviceIDs[index]); int serviceIndex = servicesMap.find(serviceIDs[index]);
if (serviceIndex == -1) if (serviceIndex == -1)
{ {
throw std::runtime_error("Service not found!"); throw std::runtime_error("Service not found!");
} }
Service* service = trackedServicesMap.getValueAt(serviceIndex).data; Service* service = servicesMap.getValueAt(serviceIndex);
selectedServices[service->getId()] = service; selectedServices[service->getId()] = service;
} }
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0); ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0);
@@ -67,7 +66,7 @@ void ServiceManagementService::purchaseService(const util::Vector<std::string>&
{ {
throw std::runtime_error("Failed to create service booking"); throw std::runtime_error("Failed to create service booking");
} }
trackedServiceBookingMap[serviceBooking->getId()] = util::createNewRecord(serviceBooking); serviceBookingMap[serviceBooking->getId()] = serviceBooking;
std::string title = "Service Booking succeeded"; std::string title = "Service Booking succeeded";
std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId(); std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId();
sendNotification(authenticatedUser, title, message); sendNotification(authenticatedUser, title, message);
@@ -92,22 +91,21 @@ void ServiceManagementService::purchaseComboPackage(const std::string& comboPack
{ {
throw std::runtime_error("No user is currently logged in!"); throw std::runtime_error("No user is currently logged in!");
} }
DataStoreLockGuard lock(m_dataStore); auto& comboPackagesMap = m_dataStore.getComboPackages();
auto& trackedComboPackagesMap = m_dataStore.getComboPackages(); auto& serviceBookingMap = m_dataStore.getServiceBookings();
auto& trackedServiceBookingMap = m_dataStore.getServiceBookings(); int comboPackageIndex = comboPackagesMap.find(comboPackageID);
int comboPackageIndex = trackedComboPackagesMap.find(comboPackageID);
if (comboPackageIndex == -1) if (comboPackageIndex == -1)
{ {
throw std::runtime_error("Combo Package not found!"); throw std::runtime_error("Combo Package not found!");
} }
const ComboPackage* comboPackage = trackedComboPackagesMap[comboPackageID].data; const ComboPackage* comboPackage = comboPackagesMap[comboPackageID];
util::Map<std::string, Service*> selectedServices = comboPackage->getServices(); util::Map<std::string, Service*> selectedServices = comboPackage->getServices();
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage()); ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage());
if (serviceBooking == nullptr) if (serviceBooking == nullptr)
{ {
throw std::runtime_error("Failed to create combo package service booking"); throw std::runtime_error("Failed to create combo package service booking");
} }
trackedServiceBookingMap[serviceBooking->getId()] = util::createNewRecord(serviceBooking); serviceBookingMap[serviceBooking->getId()] = serviceBooking;
std::string title = "Combo Package Service Booking succeeded"; std::string title = "Combo Package Service Booking succeeded";
std::string message = "Your service booking for the combo package has been successfully placed with ID " + serviceBooking->getId(); std::string message = "Your service booking for the combo package has been successfully placed with ID " + serviceBooking->getId();
sendNotification(authenticatedUser, title, message); sendNotification(authenticatedUser, title, message);
@@ -201,6 +199,299 @@ void ServiceManagementService::sendNotification(User* user, const std::string& t
m_dataStore.saveNotifications(); m_dataStore.saveNotifications();
} }
/*
Function: loadServices
Description: Loads services from persistent storage into the datastore.
Validates required inventory items and attaches them to each service.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if an inventory item ID is invalid
*/
void ServiceManagementService::loadServices()
{
util::FileManager<Service> serviceFileManager(config::file::SERVICE_FILE);
auto& services = m_dataStore.getServices();
auto& inventoryItems = m_dataStore.getInventoryItems();
auto servicesMap = serviceFileManager.load();
for (int serviceIndex = 0; serviceIndex < servicesMap.getSize(); serviceIndex++)
{
Service* service = servicesMap.getValueAt(serviceIndex);
services[service->getId()] = service;
util::Map<std::string, InventoryItem*> inventoryItemsMap;
auto& inventoryItemIDs = service->getRequiredInventoryItemIDs();
for (int inventoryItemIndex = 0; inventoryItemIndex < inventoryItemIDs.getSize(); inventoryItemIndex++)
{
const std::string& inventoryItemID = inventoryItemIDs[inventoryItemIndex];
int index = inventoryItems.find(inventoryItemID);
if (index == -1)
{
throw std::runtime_error("Invalid Inventory Item ID");
}
inventoryItemsMap[inventoryItemID] = inventoryItems.getValueAt(index);
}
service->setRequiredInventoryItems(inventoryItemsMap);
}
}
/*
Function: saveServices
Description: Saves services from the datastore to persistent storage.
Uses FileManager to serialize services into the configured file.
Parameters:
- None
Returns:
- void
*/
void ServiceManagementService::saveServices()
{
util::FileManager<Service> serviceFileManager(config::file::SERVICE_FILE);
auto& services = m_dataStore.getServices();
serviceFileManager.save(services);
}
/*
Function: loadComboPackages
Description: Loads combo packages from persistent storage into the datastore.
Validates associated services and attaches them to each package.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if a service ID is invalid
*/
void ServiceManagementService::loadComboPackages()
{
util::FileManager<ComboPackage> comboPackageFileManager(config::file::COMBOPACKAGE_FILE);
auto& comboPackages = m_dataStore.getComboPackages();
auto& services = m_dataStore.getServices();
auto comboPackagesMap = comboPackageFileManager.load();
for (int packageIndex = 0; packageIndex < comboPackagesMap.getSize(); packageIndex++)
{
ComboPackage* comboPackage = comboPackagesMap.getValueAt(packageIndex);
util::Map<std::string, Service*> packageServices;
auto& serviceIDs = comboPackage->getServiceIDs();
for (int serviceIndex = 0; serviceIndex < serviceIDs.getSize(); serviceIndex++)
{
const std::string& serviceID = serviceIDs[serviceIndex];
int serviceMapIndex = services.find(serviceID);
if (serviceMapIndex == -1)
{
throw std::runtime_error("Invalid Service ID");
}
packageServices[serviceID] = services.getValueAt(serviceMapIndex);
}
comboPackage->setServices(packageServices);
comboPackages[comboPackage->getId()] = comboPackage;
}
}
/*
Function: saveComboPackages
Description: Saves combo packages from the datastore to persistent storage.
Uses FileManager to serialize combo packages into the configured file.
Parameters:
- None
Returns:
- void
*/
void ServiceManagementService::saveComboPackages()
{
util::FileManager<ComboPackage> comboPackageFileManager(config::file::COMBOPACKAGE_FILE);
auto& comboPackages = m_dataStore.getComboPackages();
comboPackageFileManager.save(comboPackages);
}
/*
Function: loadServiceBookings
Description: Loads service bookings from persistent storage into the datastore.
Validates associated services, customers, and technicians before
attaching them to each booking.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if a service ID, customer ID, or technician ID is invalid
- std::runtime_error if a user is not of the expected type (customer/technician)
*/
void ServiceManagementService::loadServiceBookings()
{
util::FileManager<ServiceBooking> bookingFileManager(config::file::SERVICEBOOKING_FILE);
auto& serviceBookings = m_dataStore.getServiceBookings();
auto& services = m_dataStore.getServices();
auto& users = m_dataStore.getUsers();
auto bookingsMap = bookingFileManager.load();
for (int bookingIndex = 0; bookingIndex < bookingsMap.getSize(); bookingIndex++)
{
ServiceBooking* booking = bookingsMap.getValueAt(bookingIndex);
util::Map<std::string, Service*> bookingServices;
auto& serviceIDs = booking->getServiceIDs();
for (int serviceIndex = 0; serviceIndex < serviceIDs.getSize(); serviceIndex++)
{
const std::string& serviceID = serviceIDs[serviceIndex];
int serviceMapIndex = services.find(serviceID);
if (serviceMapIndex == -1)
{
throw std::runtime_error("Invalid Service ID");
}
bookingServices[serviceID] = services.getValueAt(serviceMapIndex);
}
booking->setServices(bookingServices);
int customerIndex = users.find(booking->getCustomerId());
if (customerIndex == -1)
{
throw std::runtime_error("Invalid Customer ID");
}
User* customer = users.getValueAt(customerIndex);
if (customer->getUserType() != util::UserType::CUSTOMER)
{
throw std::runtime_error("User is not a customer");
}
booking->setCustomer(customer);
const std::string& technicianId = booking->getAssignedTechnicianId();
if (!technicianId.empty())
{
int technicianIndex = users.find(technicianId);
if (technicianIndex == -1)
{
throw std::runtime_error("Invalid Technician ID");
}
User* technician = users.getValueAt(technicianIndex);
if (technician->getUserType() != util::UserType::TECHNICIAN)
{
throw std::runtime_error("User is not a technician");
}
booking->setAssignedTechnician(technician);
}
serviceBookings[booking->getId()] = booking;
}
}
/*
Function: saveServiceBookings
Description: Saves service bookings from the datastore to persistent storage.
Uses FileManager to serialize bookings into the configured file.
Parameters:
- None
Returns:
- void
*/
void ServiceManagementService::saveServiceBookings()
{
util::FileManager<ServiceBooking> bookingFileManager(config::file::SERVICEBOOKING_FILE);
auto& serviceBookings = m_dataStore.getServiceBookings();
bookingFileManager.save(serviceBookings);
}
/*
Function: loadJobCards
Description: Loads job cards from persistent storage into the datastore.
Validates associated bookings, services, and technicians before
attaching them to each job card.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if a booking ID, service ID, or technician ID is invalid
- std::runtime_error if a service does not belong to the booking
- std::runtime_error if a user is not a technician
*/
void ServiceManagementService::loadJobCards()
{
util::FileManager<JobCard> jobCardFileManager(config::file::JOBCARD_FILE);
auto& jobCards = m_dataStore.getJobCards();
auto& serviceBookings = m_dataStore.getServiceBookings();
auto& services = m_dataStore.getServices();
auto& users = m_dataStore.getUsers();
auto jobCardsMap = jobCardFileManager.load();
for (int jobCardIndex = 0; jobCardIndex < jobCardsMap.getSize(); jobCardIndex++)
{
JobCard* jobCard = jobCardsMap.getValueAt(jobCardIndex);
int bookingIndex = serviceBookings.find(jobCard->getBookingId());
if (bookingIndex == -1)
{
throw std::runtime_error("Invalid Booking ID");
}
ServiceBooking* booking = serviceBookings.getValueAt(bookingIndex);
jobCard->setBooking(booking);
int serviceIndex = services.find(jobCard->getServiceId());
if (serviceIndex == -1)
{
throw std::runtime_error("Invalid Service ID");
}
Service* service = services.getValueAt(serviceIndex);
if (booking->getServices().find(jobCard->getServiceId()) == -1)
{
throw std::runtime_error("Service does not belong to booking");
}
jobCard->setService(service);
int technicianIndex = users.find(jobCard->getTechnicianId());
if (technicianIndex == -1)
{
throw std::runtime_error("Invalid Technician ID");
}
User* technician = users.getValueAt(technicianIndex);
if (technician->getUserType() != util::UserType::TECHNICIAN)
{
throw std::runtime_error("User is not a technician");
}
jobCard->setTechnician(technician);
jobCards[jobCard->getId()] = jobCard;
}
}
/*
Function: saveJobCards
Description: Saves job cards from the datastore to persistent storage.
Uses FileManager to serialize job cards into the configured file.
Parameters:
- None
Returns:
- void
*/
void ServiceManagementService::saveJobCards()
{
util::FileManager<JobCard> jobCardFileManager(config::file::JOBCARD_FILE);
auto& jobCards = m_dataStore.getJobCards();
jobCardFileManager.save(jobCards);
}
/*
Function: loadObservers
Description: Loads observer IDs from persistent storage and attaches corresponding
users as observers to the ServiceManagementService.
Parameters:
- None
Returns:
- void
Throws:
- std::runtime_error if an observer ID is invalid (not found in datastore)
*/
void ServiceManagementService::loadObservers()
{
util::loadObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this, m_dataStore);
}
/*
Function: saveObservers
Description: Saves the current observer IDs of the ServiceManagementService
to persistent storage for future retrieval.
Parameters:
- None
Returns:
- void
*/
void ServiceManagementService::saveObservers()
{
util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this);
}
/* /*
Function: restoreInventory Function: restoreInventory
Description: Restores inventory quantities for all required items in the services associated Description: Restores inventory quantities for all required items in the services associated
@@ -208,7 +499,7 @@ Description: Restores inventory quantities for all required items in the service
Parameter: ServiceBooking* booking - Pointer to the booking whose inventory items need to be restored Parameter: ServiceBooking* booking - Pointer to the booking whose inventory items need to be restored
Return type: void Return type: void
*/ */
static void restoreInventory(ServiceBooking* booking, util::Map<std::string, TrackedRecord<InventoryItem>>& trackedInventoryItems) static void restoreInventory(ServiceBooking* booking)
{ {
const int INCREMENT_VALUE = 1; const int INCREMENT_VALUE = 1;
if (!booking) if (!booking)
@@ -227,17 +518,9 @@ static void restoreInventory(ServiceBooking* booking, util::Map<std::string, Tra
for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++InventoryIterator) for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++InventoryIterator)
{ {
InventoryItem* item = items.getValueAt(InventoryIterator); InventoryItem* item = items.getValueAt(InventoryIterator);
const std::string& currentItemId = item->getId();
int itemIndex = trackedInventoryItems.find(currentItemId);
if (itemIndex == -1)
{
continue;
}
auto& currentTrackedInventoryItem = trackedInventoryItems.getValueAt(itemIndex);
if (item) if (item)
{ {
item->setQuantity(item->getQuantity() + INCREMENT_VALUE); item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
currentTrackedInventoryItem.state = RecordState::MODIFIED;
} }
} }
} }
@@ -255,28 +538,23 @@ Parameters:
util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN) util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN)
Return type: void Return type: void
*/ */
static void processBookingCancellation(TrackedRecord<ServiceBooking>& trackedBooking, static void processBookingCancellation(ServiceBooking* booking,
util::Map<std::string, TrackedRecord<JobCard>>& jobs, util::Map<std::string, JobCard*>& jobs,
ServiceManagementService& currentService, ServiceManagementService& currentService,
util::UserType userType, util::UserType userType)
util::Map<std::string, TrackedRecord<InventoryItem>>& trackedInventoryItems)
{ {
ServiceBooking* booking = trackedBooking.data;
if (!booking) if (!booking)
{ {
return; return;
} }
const std::string& bookingId = booking->getId();
for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator) for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator)
{ {
auto& trackedJobCard = jobs.getValueAt(jobIterator); JobCard* jobCard = jobs.getValueAt(jobIterator);
JobCard* jobCard = trackedJobCard.data;
if (!jobCard || jobCard->getBookingId() != booking->getId() || jobCard->getStatus() == util::ServiceJobStatus::CANCELLED) if (!jobCard || jobCard->getBookingId() != booking->getId() || jobCard->getStatus() == util::ServiceJobStatus::CANCELLED)
{ {
continue; continue;
} }
jobCard->setStatus(util::ServiceJobStatus::CANCELLED); jobCard->setStatus(util::ServiceJobStatus::CANCELLED);
trackedJobCard.state = RecordState::MODIFIED;
if (userType == util::UserType::CUSTOMER) if (userType == util::UserType::CUSTOMER)
{ {
if (User* technician = booking->getAssignedTechnician()) if (User* technician = booking->getAssignedTechnician())
@@ -309,8 +587,7 @@ static void processBookingCancellation(TrackedRecord<ServiceBooking>& trackedBoo
} }
booking->setAssignedTechnician(nullptr); booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId(""); booking->setAssignedTechnicianId("");
trackedBooking.state = RecordState::MODIFIED; restoreInventory(booking);
restoreInventory(booking, trackedInventoryItems);
} }
/* /*
@@ -323,25 +600,22 @@ Return type: void
*/ */
void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID) void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID)
{ {
DataStoreLockGuard lock(m_dataStore); auto& users = m_dataStore.getUsers();
auto& trackedUsers = m_dataStore.getUsers(); int userIndex = users.find(customerID);
int userIndex = trackedUsers.find(customerID);
if (userIndex == -1) if (userIndex == -1)
{ {
throw std::runtime_error("User not found: " + customerID); throw std::runtime_error("User not found: " + customerID);
} }
User* customer = trackedUsers.getValueAt(userIndex).data; User* customer = users.getValueAt(userIndex);
if (!customer) if (!customer)
{ {
throw std::runtime_error("User not found: " + customerID); throw std::runtime_error("User not found: " + customerID);
} }
auto& trackedBookings = m_dataStore.getServiceBookings(); auto& bookings = m_dataStore.getServiceBookings();
auto& trackedJobs = m_dataStore.getJobCards(); auto& jobs = m_dataStore.getJobCards();
auto& trackedInventoryItems = m_dataStore.getInventoryItems(); for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++)
{ {
auto& trackedBooking = trackedBookings.getValueAt(iteratorOne); ServiceBooking* booking = bookings.getValueAt(iteratorOne);
ServiceBooking* booking = trackedBooking.data;
if (!booking) if (!booking)
{ {
continue; continue;
@@ -356,12 +630,8 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string&
{ {
continue; continue;
} }
processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::CUSTOMER, trackedInventoryItems); processBookingCancellation(booking, jobs, *this, util::UserType::CUSTOMER);
} }
m_dataStore.saveUsers();
m_dataStore.saveServiceBookings();
m_dataStore.saveJobCards();
m_dataStore.saveInventoryItems();
} }
/* /*
@@ -373,25 +643,22 @@ Return type: void
*/ */
void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID) void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID)
{ {
DataStoreLockGuard lock(m_dataStore); auto& users = m_dataStore.getUsers();
auto& trackedUsers = m_dataStore.getUsers(); int userIndex = users.find(technicianID);
int userIndex = trackedUsers.find(technicianID);
if (userIndex == -1) if (userIndex == -1)
{ {
throw std::runtime_error("User not found: " + technicianID); throw std::runtime_error("User not found: " + technicianID);
} }
User* technician = trackedUsers.getValueAt(userIndex).data; User* technician = users.getValueAt(userIndex);
if (!technician) if (!technician)
{ {
throw std::runtime_error("User not found: " + technicianID); throw std::runtime_error("User not found: " + technicianID);
} }
auto& trackedBookings = m_dataStore.getServiceBookings(); auto& bookings = m_dataStore.getServiceBookings();
auto& trackedJobs = m_dataStore.getJobCards(); auto& jobs = m_dataStore.getJobCards();
auto& trackedInventoryItems = m_dataStore.getInventoryItems(); for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++)
{ {
auto& trackedBooking = trackedBookings.getValueAt(iteratorOne); ServiceBooking* booking = bookings.getValueAt(iteratorOne);
ServiceBooking* booking = trackedBooking.data;
if (!booking) if (!booking)
{ {
continue; continue;
@@ -412,12 +679,8 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia
{ {
continue; continue;
} }
processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::TECHNICIAN, trackedInventoryItems); processBookingCancellation(booking, jobs, *this, util::UserType::TECHNICIAN);
} }
m_dataStore.saveUsers();
m_dataStore.saveInventoryItems();
m_dataStore.saveServiceBookings();
m_dataStore.saveJobCards();
} }
/* /*
@@ -431,7 +694,6 @@ Return type: void
*/ */
void ServiceManagementService::createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDsInNewCombo, double discountPercentage) void ServiceManagementService::createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDsInNewCombo, double discountPercentage)
{ {
DataStoreLockGuard lock(m_dataStore);
if (packageName.empty()) if (packageName.empty())
{ {
throw std::invalid_argument("The Combo Package Name cannot be empty.\n"); throw std::invalid_argument("The Combo Package Name cannot be empty.\n");
@@ -444,19 +706,19 @@ void ServiceManagementService::createComboPackage(const std::string& packageName
{ {
throw std::invalid_argument("Discount percentage must be between 0 and 100."); throw std::invalid_argument("Discount percentage must be between 0 and 100.");
} }
auto& trackedServicesMap = m_dataStore.getServices(); auto& servicesMap = m_dataStore.getServices();
for (int index = 0; index < serviceIDsInNewCombo.getSize(); index++) for (int index = 0; index < serviceIDsInNewCombo.getSize(); index++)
{ {
const std::string& serviceid = serviceIDsInNewCombo[index]; const std::string serviceid = serviceIDsInNewCombo[index];
if (trackedServicesMap.find(serviceid) == -1) if (servicesMap.find(serviceid) == -1)
{ {
throw std::runtime_error("Service ID not found: " + serviceid); throw std::runtime_error("Service ID not found: " + serviceid);
} }
} }
auto& trackedComboPackageMap = m_dataStore.getComboPackages(); auto& comboPackageMap = m_dataStore.getComboPackages();
for (int iterator = 0; iterator < trackedComboPackageMap.getSize(); iterator++) for (int iterator = 0; iterator < comboPackageMap.getSize(); iterator++)
{ {
ComboPackage* existingCombos = trackedComboPackageMap.getValueAt(iterator).data; ComboPackage* existingCombos = comboPackageMap.getValueAt(iterator);
const util::Map<std::string, Service*>& servicesInsideExistingCombos = existingCombos->getServices(); const util::Map<std::string, Service*>& servicesInsideExistingCombos = existingCombos->getServices();
if (servicesInsideExistingCombos.getSize() == serviceIDsInNewCombo.getSize()) if (servicesInsideExistingCombos.getSize() == serviceIDsInNewCombo.getSize())
{ {
@@ -480,16 +742,15 @@ void ServiceManagementService::createComboPackage(const std::string& packageName
for (int iteratorOne = 0; iteratorOne < serviceIDsInNewCombo.getSize(); iteratorOne++) for (int iteratorOne = 0; iteratorOne < serviceIDsInNewCombo.getSize(); iteratorOne++)
{ {
const std::string& serviceId = serviceIDsInNewCombo[iteratorOne]; const std::string& serviceId = serviceIDsInNewCombo[iteratorOne];
int serviceIndex = trackedServicesMap.find(serviceId); int serviceIndex = servicesMap.find(serviceId);
if (serviceIndex == -1) if (serviceIndex == -1)
{ {
throw std::runtime_error("Service ID not found: " + serviceId); throw std::runtime_error("Service ID not found: " + serviceId);
} }
selectedServices.insert(serviceId, trackedServicesMap.getValueAt(serviceIndex).data); selectedServices.insert(serviceId, servicesMap.getValueAt(serviceIndex));
} }
ComboPackage* newComboPackage = Factory::getObject<ComboPackage>(packageName, discountPercentage, selectedServices); ComboPackage* newComboPackage = Factory::getObject<ComboPackage>(packageName, discountPercentage, selectedServices);
trackedComboPackageMap.insert(newComboPackage->getId(), util::createNewRecord(newComboPackage)); comboPackageMap.insert(newComboPackage->getId(), newComboPackage);
m_dataStore.saveComboPackages();
} }
/* /*
@@ -500,10 +761,7 @@ Return type: util::Map<std::string, ComboPackage*>
*/ */
util::Map<std::string, ComboPackage*> ServiceManagementService::getComboPackages() util::Map<std::string, ComboPackage*> ServiceManagementService::getComboPackages()
{ {
DataStoreLockGuard lock(m_dataStore); return m_dataStore.getComboPackages();
util::Map<std::string, ComboPackage*> comboPackages;
comboPackages = util::getObjects(m_dataStore.getComboPackages());
return comboPackages;
} }
/* /*
@@ -514,17 +772,14 @@ Return type: void
*/ */
void ServiceManagementService::removeComboPackage(const std::string& comboPackageID) void ServiceManagementService::removeComboPackage(const std::string& comboPackageID)
{ {
DataStoreLockGuard lock(m_dataStore);
bool removed = false; bool removed = false;
auto& trackedComboPackages = m_dataStore.getComboPackages(); util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
for (int iterator = 0; iterator < trackedComboPackages.getSize(); iterator++) for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
{ {
auto& comboPackage = trackedComboPackages.getValueAt(iterator); ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
ComboPackage* currentComboPackage = comboPackage.data;
if (currentComboPackage && currentComboPackage->getId() == comboPackageID) if (currentComboPackage && currentComboPackage->getId() == comboPackageID)
{ {
currentComboPackage->setState(util::State::INACTIVE); currentComboPackage->setState(util::State::INACTIVE);
comboPackage.state = RecordState::MODIFIED;
removed = true; removed = true;
break; break;
} }
@@ -533,7 +788,6 @@ void ServiceManagementService::removeComboPackage(const std::string& comboPackag
{ {
throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found."); throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found.");
} }
m_dataStore.saveComboPackages();
} }
/* /*
@@ -546,10 +800,7 @@ Returns:
*/ */
util::Map<std::string, ServiceBooking*> ServiceManagementService::getServiceBookings() util::Map<std::string, ServiceBooking*> ServiceManagementService::getServiceBookings()
{ {
DataStoreLockGuard lock(m_dataStore); return m_dataStore.getServiceBookings();
util::Map<std::string, ServiceBooking*> serviceBookings;
serviceBookings = util::getObjects(m_dataStore.getServiceBookings());
return serviceBookings;
} }
/* /*
@@ -588,10 +839,9 @@ Throws:
*/ */
void ServiceManagementService::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID) void ServiceManagementService::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID)
{ {
DataStoreLockGuard lock(m_dataStore);
UserManagementService m_userManagementService; UserManagementService m_userManagementService;
ServiceBooking* currentBooking = getServiceBooking(bookingID); ServiceBooking* currentBooking = getServiceBooking(bookingID);
auto& currentTrackedJobCards = m_dataStore.getJobCards(); auto& currentJobCards = m_dataStore.getJobCards();
if (currentBooking == nullptr) if (currentBooking == nullptr)
{ {
throw std::runtime_error("Service Booking not available"); throw std::runtime_error("Service Booking not available");
@@ -637,7 +887,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
JobCard* jobCard = Factory::getObject<JobCard>(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp()); JobCard* jobCard = Factory::getObject<JobCard>(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::Timestamp(), util::ServiceJobStatus::STARTED, util::Timestamp());
if (jobCard) if (jobCard)
{ {
currentTrackedJobCards.insert(jobCard->getId(), util::createNewRecord(jobCard)); currentJobCards.insert(jobCard->getId(), jobCard);
sendNotification(selectedTechnician, title, message); sendNotification(selectedTechnician, title, message);
} }
else else
@@ -647,7 +897,6 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
title = "Technician assigned"; title = "Technician assigned";
message = "A technician has been assigned to your Service Booking with ID " + bookingID; message = "A technician has been assigned to your Service Booking with ID " + bookingID;
sendNotification(currentBooking->getCustomer(), title, message); sendNotification(currentBooking->getCustomer(), title, message);
m_dataStore.saveJobCards();
} }
/* /*
@@ -665,17 +914,15 @@ Throws:
*/ */
void ServiceManagementService::createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost) void ServiceManagementService::createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost)
{ {
DataStoreLockGuard lock(m_dataStore);
util::Map<std::string, InventoryItem*> currentServiceInventoryItems; util::Map<std::string, InventoryItem*> currentServiceInventoryItems;
auto& trackedInventoryItems = m_dataStore.getInventoryItems(); auto inventoryItems = m_dataStore.getInventoryItems();
auto& currentServices = m_dataStore.getServices();
for (int iteratorOne =0; iteratorOne < inventoryItemIDs.getSize(); iteratorOne++) for (int iteratorOne =0; iteratorOne < inventoryItemIDs.getSize(); iteratorOne++)
{ {
std::string currentItemID = inventoryItemIDs[iteratorOne]; std::string currentItemID = inventoryItemIDs[iteratorOne];
bool itemFound = false; bool itemFound = false;
for (int iteratorTwo = 0; iteratorTwo < trackedInventoryItems.getSize(); iteratorTwo++) for (int iteratorTwo = 0; iteratorTwo < inventoryItems.getSize(); iteratorTwo++)
{ {
InventoryItem* currentInventoryItem = trackedInventoryItems.getValueAt(iteratorTwo).data; InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iteratorTwo);
if (currentInventoryItem && currentInventoryItem->getId() == currentItemID) if (currentInventoryItem && currentInventoryItem->getId() == currentItemID)
{ {
itemFound = true; itemFound = true;
@@ -693,12 +940,12 @@ void ServiceManagementService::createService(const std::string& name, const util
{ {
throw std::runtime_error("Unable to create new service."); throw std::runtime_error("Unable to create new service.");
} }
util::Map<std::string, Service*>& currentServices = m_dataStore.getServices();
if (currentServices.find(newService->getId()) != -1) if (currentServices.find(newService->getId()) != -1)
{ {
throw std::runtime_error("Service with this ID Already exists."); throw std::runtime_error("Service with this ID Already exists.");
} }
currentServices.insert(newService->getId(), util::createNewRecord(newService)); currentServices.insert(newService->getId(), newService);
m_dataStore.saveServices();
} }
/* /*
@@ -711,10 +958,7 @@ Returns:
*/ */
util::Map<std::string, Service*> ServiceManagementService::getServices() util::Map<std::string, Service*> ServiceManagementService::getServices()
{ {
DataStoreLockGuard lock(m_dataStore); return m_dataStore.getServices();
util::Map<std::string, Service*> services;
services = util::getObjects(m_dataStore.getServices());
return services;
} }
/* /*
@@ -729,19 +973,14 @@ Throws:
*/ */
void ServiceManagementService::removeService(const std::string& serviceID) void ServiceManagementService::removeService(const std::string& serviceID)
{ {
DataStoreLockGuard lock(m_dataStore); util::Map<std::string, Service*>& currentServices = m_dataStore.getServices();
auto& currentTrackedServices = m_dataStore.getServices(); util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
auto& currentTrackedComboPackages = m_dataStore.getComboPackages(); if (currentServices.find(serviceID) != -1)
if (currentTrackedServices.find(serviceID) != -1)
{ {
int serviceIndex, comboPackageIndex; currentServices.getValueAt(currentServices.find(serviceID))->setState(util::State::INACTIVE);
serviceIndex = currentTrackedServices.find(serviceID); for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
currentTrackedServices.getValueAt(serviceIndex).data->setState(util::State::INACTIVE);
currentTrackedServices.getValueAt(serviceIndex).state = RecordState::MODIFIED;
for (int iterator = 0; iterator < currentTrackedComboPackages.getSize(); iterator++)
{ {
comboPackageIndex = iterator; ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
ComboPackage* currentComboPackage = currentTrackedComboPackages.getValueAt(iterator).data;
if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE) if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE)
{ {
util::Map<std::string, Service*> currentServices = currentComboPackage->getServices(); util::Map<std::string, Service*> currentServices = currentComboPackage->getServices();
@@ -751,7 +990,6 @@ void ServiceManagementService::removeService(const std::string& serviceID)
if (currentService->getId() == serviceID) if (currentService->getId() == serviceID)
{ {
currentComboPackage->setState(util::State::INACTIVE); currentComboPackage->setState(util::State::INACTIVE);
currentTrackedComboPackages.getValueAt(comboPackageIndex).state = RecordState::MODIFIED;
break; break;
} }
} }
@@ -762,8 +1000,6 @@ void ServiceManagementService::removeService(const std::string& serviceID)
{ {
throw std::runtime_error("Service not found."); throw std::runtime_error("Service not found.");
} }
m_dataStore.saveServices();
m_dataStore.saveComboPackages();
} }
/* /*
@@ -802,12 +1038,11 @@ Returns:
*/ */
util::Map<std::string, JobCard*> ServiceManagementService::getJobCards(const std::string& technicianID) util::Map<std::string, JobCard*> ServiceManagementService::getJobCards(const std::string& technicianID)
{ {
DataStoreLockGuard lock(m_dataStore); util::Map<std::string, JobCard*> jobCards = m_dataStore.getJobCards();
auto& trackedJobCards = m_dataStore.getJobCards();
util::Map<std::string, JobCard*> technicianJobCards; util::Map<std::string, JobCard*> technicianJobCards;
for (int iterator = 0; iterator < trackedJobCards.getSize(); iterator++) for (int iterator = 0; iterator < jobCards.getSize(); iterator++)
{ {
JobCard* currentJobCard = trackedJobCards.getValueAt(iterator).data; JobCard* currentJobCard = jobCards.getValueAt(iterator);
if (currentJobCard->getTechnicianId() == technicianID) if (currentJobCard->getTechnicianId() == technicianID)
{ {
technicianJobCards.insert(currentJobCard->getId(), currentJobCard); technicianJobCards.insert(currentJobCard->getId(), currentJobCard);
@@ -856,7 +1091,6 @@ Returns:
*/ */
void ServiceManagementService::updateJobStatus(const std::string& jobID) void ServiceManagementService::updateJobStatus(const std::string& jobID)
{ {
DataStoreLockGuard lock(m_dataStore);
AuthenticationManagementService authenticationManagementService; AuthenticationManagementService authenticationManagementService;
PaymentManagementService paymentManagementService; PaymentManagementService paymentManagementService;
bool jobStatusUpdated = false, serviceBookingCompleted; bool jobStatusUpdated = false, serviceBookingCompleted;
@@ -871,15 +1105,8 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
{ {
throw std::runtime_error("No job cards assigned to the technician."); throw std::runtime_error("No job cards assigned to the technician.");
} }
auto& trackedJobCards = m_dataStore.getJobCards();
auto& trackedServiceBookings = m_dataStore.getServiceBookings();
if (currentAssignedJobs.find(jobID) != -1) if (currentAssignedJobs.find(jobID) != -1)
{ {
int jobIndex = trackedJobCards.find(jobID);
if (jobIndex == -1)
{
throw std::runtime_error("Unable to fetch current job.");
}
currentJob = currentAssignedJobs.getValueAt(currentAssignedJobs.find(jobID)); currentJob = currentAssignedJobs.getValueAt(currentAssignedJobs.find(jobID));
if (currentJob == nullptr) if (currentJob == nullptr)
{ {
@@ -888,20 +1115,16 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED) if (currentJob->getStatus() == util::ServiceJobStatus::STARTED)
{ {
currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS); currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS);
trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED;
jobStatusUpdated = true; jobStatusUpdated = true;
} }
else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS) else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS)
{ {
currentJob->setStatus(util::ServiceJobStatus::COMPLETED); currentJob->setStatus(util::ServiceJobStatus::COMPLETED);
trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED;
jobStatusUpdated = true; jobStatusUpdated = true;
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
if (serviceBookingCompleted) if (serviceBookingCompleted)
{ {
const std::string& bookingId = currentJob->getBookingId();
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED); currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
trackedServiceBookings.getValueAt(trackedServiceBookings.find(bookingId)).state = RecordState::MODIFIED;
paymentManagementService.generateInvoice(currentJob->getBooking()); paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed. Invoice Generated."; std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated."; std::string message = "Services completed for the booking and invoice generated.";
@@ -917,6 +1140,4 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
{ {
throw std::runtime_error("Failed to update job status. Job may already be completed."); throw std::runtime_error("Failed to update job status. Job may already be completed.");
} }
m_dataStore.saveJobCards();
m_dataStore.saveServiceBookings();
} }
@@ -23,6 +23,7 @@ class ServiceManagementService : public NotificationManagementService
private: private:
DataStore& m_dataStore; DataStore& m_dataStore;
static util::Map<std::string, User*> m_observers; static util::Map<std::string, User*> m_observers;
util::Vector<std::string> getObserverIDs() override;
public: public:
ServiceManagementService() : m_dataStore(DataStore::getInstance()) {} ServiceManagementService() : m_dataStore(DataStore::getInstance()) {}
util::Map<std::string, Service*> getServices(); util::Map<std::string, Service*> getServices();
@@ -44,4 +45,12 @@ 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 loadServices();
void saveServices();
void loadComboPackages();
void saveComboPackages();
void loadServiceBookings();
void saveServiceBookings();
void loadJobCards();
void saveJobCards();
}; };
@@ -11,6 +11,7 @@ Date:19-May-2026
#include "Config.h" #include "Config.h"
#include "Enums.h" #include "Enums.h"
#include "Factory.h" #include "Factory.h"
#include "FileManager.h"
#include "InventoryManagementService.h" #include "InventoryManagementService.h"
#include "Notification.h" #include "Notification.h"
#include "PaymentManagementService.h" #include "PaymentManagementService.h"
@@ -180,6 +181,7 @@ util::Vector<Notification*> UserManagementService::getUserNotifications(const st
notificationsVector.push_back(notification); notificationsVector.push_back(notification);
} }
} }
m_dataStore.unlockDataStore();
return notificationsVector; return notificationsVector;
} }
else else
@@ -211,18 +213,15 @@ void UserManagementService::deleteNotification(const std::string& notificationID
{ {
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;
int notificationIndex = trackedNotificationsMap.find(notificationID); int notificationIndex = trackedNotificationsMap.find(notificationID);
if (notificationIndex == -1) if (notificationIndex == -1)
{ {
throw std::runtime_error("No notification found with given NotificationID"); throw std::runtime_error("No notification found with given NotificationID");
} }
Notification* notification = trackedNotificationsMap.getValueAt(notificationIndex).data; trackedNotificationsMap.getValueAt(notificationIndex).data->setState(util::State::INACTIVE);
if (notification->getRecipientUserId() == userID) trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED;
{ m_dataStore.saveNotifications();
notification->setState(util::State::INACTIVE);
trackedNotificationsMap.getValueAt(notificationIndex).state = RecordState::MODIFIED;
m_dataStore.saveNotifications();
}
} }
/* /*
@@ -43,4 +43,67 @@ namespace util
position++; position++;
} }
} }
/*
Function: loadRecords
Description: Loads records from a given file path into a vector of strings.
Skips the header line if present. Creates the file if it does not exist.
Parameters:
- filePath: const std::string&, path to the file
Returns:
- util::Vector<std::string>: Vector containing all records (excluding header)
Throws:
- None (creates file if missing)
*/
inline util::Vector<std::string> loadRecords(const std::string& filePath)
{
util::Vector<std::string> records;
std::ifstream file(filePath);
if (!file.is_open())
{
ensureDirectoryExists(filePath);
std::ofstream newFile(filePath);
newFile.close();
file.open(filePath);
}
std::string line;
bool isHeader = true;
while (std::getline(file, line))
{
if (isHeader)
{
isHeader = false;
continue;
}
records.push_back(line);
}
return records;
}
/*
Function: saveRecords
Description: Saves records to a given file path. Overwrites existing content
and writes a header line followed by all records.
Parameters:
- filePath: const std::string&, path to the file
- records: const util::Vector<std::string>&, vector of records to save
Returns:
- void
Throws:
- std::runtime_error if the file cannot be opened for writing
*/
inline void saveRecords(const std::string& filePath, const util::Vector<std::string>& records)
{
std::ofstream file(filePath, std::ios::trunc);
if (!file.is_open())
{
throw std::runtime_error("Failed to open file " + filePath);
}
file << "Values" << '\n';
int numberOfRecords = records.getSize();
for (int index = 0; index < numberOfRecords; index++)
{
file << records[index] << '\n';
}
}
} }
@@ -0,0 +1,119 @@
/*
File: FileManager.h
Description: Declares and implements a generic FileManager template class for
loading and saving objects to and from files. Uses serialization
and deserialization methods defined in the object type T.
Provides persistence support for system entities such as Users,
Services, InventoryItems, etc.
Author: Trenser
Date: 22-May-2026
*/
#pragma once
#include <stdexcept>
#include <string>
#include <fstream>
#include "Vector.h"
#include "Map.h"
#include "FileHelper.h"
namespace util
{
template <typename T> using objects = util::Map<std::string, T*>;
template <typename T>
class FileManager
{
private:
std::string m_filePath;
public:
FileManager() : m_filePath("") {}
FileManager(const std::string& filePath) : m_filePath(filePath) {}
objects<T> load();
void save(const objects<T>&);
};
/*
Function: load
Description: Loads records from the file into a map of objects.
Skips the header line, deserializes each record into an object of type T,
and stores them in a map keyed by object ID.
Parameters:
- None
Returns:
- util::Map<std::string, T*> containing deserialized objects
Throws:
- std::runtime_error if deserialization fails for any record
*/
template <typename T>
objects<T> FileManager<T>::load()
{
objects<T> records;
std::ifstream file(m_filePath);
if (!file.is_open())
{
ensureDirectoryExists(m_filePath);
std::ofstream newFile(m_filePath);
newFile.close();
file.open(m_filePath);
}
util::Vector<std::string> lines;
std::string line;
while (std::getline(file, line))
{
lines.push_back(line);
}
int numberOfLines = lines.getSize();
bool isHeader = true;
for (int lineIndex = 0; lineIndex < numberOfLines; lineIndex++)
{
const auto& record = lines[lineIndex];
if (isHeader)
{
isHeader = false;
continue;
}
auto object = T::deserialize(record);
if (!object)
{
throw std::runtime_error("Failed to deserialize record");
}
records[object->getId()] = object;
}
return records;
}
/*
Function: save
Description: Saves records to the file. Serializes each object of type T into a string,
writes a header line, and then writes all serialized records to the file.
Parameters:
- records: const util::Map<std::string, T*>&, map of objects to save
Returns:
- void
Throws:
- std::runtime_error if the file cannot be opened for writing
*/
template <typename T>
void FileManager<T>::save(const objects<T>& records)
{
util::Vector<std::string> lines;
lines.push_back(T::getHeaders());
int numberOfRecords = records.getSize();
for (int recordIndex = 0; recordIndex < numberOfRecords; recordIndex++)
{
const auto& record = records.getValueAt(recordIndex);
lines.push_back(record->serialize());
}
std::ofstream file(m_filePath, std::ios::trunc);
if (!file.is_open())
{
throw std::runtime_error("Failed to open file " + m_filePath);
}
int numberOfLines = lines.getSize();
for (int lineIndex = 0; lineIndex < numberOfLines; lineIndex++)
{
file << lines[lineIndex] << '\n';
}
}
}
@@ -28,7 +28,7 @@ namespace util
if (!(std::cin >> value)) if (!(std::cin >> value))
{ {
std::cin.clear(); std::cin.clear();
std::cin.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw std::runtime_error("Invalid console input"); throw std::runtime_error("Invalid console input");
} }
} }
@@ -54,6 +54,52 @@ namespace util
return cost; return cost;
} }
/*
Function: loadObservers
Description: Loads observer IDs from a file and attaches the corresponding users
to the notification management service. Validates that each observer ID
exists in the datastore before attaching.
Parameters:
- filePath: const std::string&, path to the file containing observer IDs
- service: NotificationManagementService*, pointer to the notification service
- dataStore: DataStore&, reference to the datastore containing users
Returns:
- void
Throws:
- std::runtime_error if an observer ID is invalid (not found in datastore)
*/
inline void loadObservers(const std::string& filePath, NotificationManagementService* service, DataStore& dataStore)
{
auto observerIDs = util::loadRecords(filePath);
auto& users = dataStore.getUsers();
for (int index = 0; index < observerIDs.getSize(); index++)
{
const std::string& observerID = observerIDs[index];
int userIndex = users.find(observerID);
if (userIndex == -1)
{
throw std::runtime_error("Invalid Observer ID");
}
service->attach(users.getValueAt(userIndex));
}
}
/*
Function: saveObservers
Description: Saves the current observer IDs from the notification management service
to a file for persistence.
Parameters:
- filePath: const std::string&, path to the file where observer IDs will be saved
- service: NotificationManagementService*, pointer to the notification service
Returns:
- void
*/
inline void saveObservers(const std::string& filePath, NotificationManagementService* service)
{
auto observerIDs = service->getObserverIDs();
util::saveRecords(filePath, observerIDs);
}
template<typename TObject> template<typename TObject>
Map<std::string, TObject*> getObjects(const Map<std::string, TrackedRecord<TObject>>& trackedRecords); Map<std::string, TObject*> getObjects(const Map<std::string, TrackedRecord<TObject>>& trackedRecords);