Compare commits

..

4 Commits

Author SHA1 Message Date
joelthomastrenser 6450f8e62f Fix cache refresh handling for unsaved records
- Preserve NEW_RECORD entries during cache refresh
- Prevent accidental deletion of pending datastore changes
- Clean up only stale persisted records
2026-06-14 17:00:49 +05:30
joelthomastrenser 6971663d33 Clean up legacy code
- Remove FileManager and related file-based persistence logic
- Remove obsolete observer persistence utilities
- Remove unused service APIs and includes
- Delete duplicate DataStore mutex stubs
- Perform general dead-code cleanup
2026-06-14 15:59:39 +05:30
joelthomastrenser edd0c60e40 Implement Service Refactoring
<UserStory> 1954: Implement Service Refactoring </UserStory>

UserStory #1954

<Changes>
1. Refactored notification handling to persist notifications directly in the datastore instead of maintaining notification collections within User objects.
2. Removed recipient User pointer dependencies from Notification and retained recipient user identification through recipientUserId.
3. Implemented generic observer persistence support in DataStore with shared helper methods for loading and saving observer subscriptions.
4. Added datastore-backed observer management for ServiceManagementService, PaymentManagementService, and InventoryManagementService.
5. Updated attach() and detach() operations to load, modify, and persist observer subscriptions using shared memory mappings.
6. Refactored sendNotification() implementations to create and persist Notification records directly to the datastore for subscribed observers.
7. Updated UserManagementService notification retrieval and deletion logic to operate on datastore notification records filtered by recipient user ID.
8. Removed notification ownership and observer-specific notification APIs from User and Observer classes.
9. Added configurable shared memory growth factor support and updated mapping expansion logic to use centralized configuration values.
10. Removed obsolete NotificationManagementService implementation and updated project configuration references.
11. Added DataStoreLockGuard integration for observer and notification persistence operations to ensure synchronized datastore access.
</Changes>

<Test>
N/A
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
2026-06-12 19:25:27 +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
13 changed files with 321 additions and 406 deletions
@@ -12,8 +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"
/* /*
Function: DataStore Function: DataStore
@@ -254,28 +252,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;
} }
@@ -289,28 +265,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;
} }
@@ -337,49 +291,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;
} }
@@ -393,51 +304,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.
@@ -557,7 +426,6 @@ Returns:
*/ */
void DataStore::saveServices() void DataStore::saveServices()
{ {
saveRecords<Service, SerializedService>(m_services, m_serviceCache);
} }
/* /*
@@ -570,7 +438,6 @@ Returns:
*/ */
void DataStore::saveComboPackages() void DataStore::saveComboPackages()
{ {
saveRecords<ComboPackage, SerializedComboPackage>(m_comboPackages, m_comboPackageCache);
} }
/* /*
@@ -595,7 +462,6 @@ Returns:
*/ */
void DataStore::saveServiceBookings() void DataStore::saveServiceBookings()
{ {
saveRecords<ServiceBooking, SerializedServiceBooking>(m_serviceBookings, m_serviceBookingCache);
} }
/* /*
@@ -608,7 +474,6 @@ Returns:
*/ */
void DataStore::saveJobCards() void DataStore::saveJobCards()
{ {
saveRecords<JobCard, SerializedJobCard>(m_jobCards, m_jobCardCache);
} }
/* /*
@@ -11,7 +11,6 @@ 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" #include "User.h"
#include "Notification.h" #include "Notification.h"
@@ -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";
}
@@ -39,6 +39,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();
}; };
@@ -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
{ {
@@ -35,11 +34,11 @@ public:
JobCard(); JobCard();
JobCard(const std::string& bookingId, JobCard(const std::string& bookingId,
ServiceBooking* booking, ServiceBooking* booking,
Service* service, Service* service,
const std::string& serviceId, const std::string& serviceId,
const std::string& technicianId, const std::string& technicianId,
User* technician, User* technician,
const util::Timestamp& assignedDate, const util::Timestamp& assignedDate,
util::ServiceJobStatus status, util::ServiceJobStatus status,
const util::Timestamp& completionDate const util::Timestamp& completionDate
); );
@@ -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
@@ -46,7 +44,7 @@ Service::Service(const std::string& name, const util::Map<std::string, Inventory
m_name(name), m_name(name),
m_requiredInventoryItems(requiredInventoryItems), m_requiredInventoryItems(requiredInventoryItems),
m_status(util::State::ACTIVE), m_status(util::State::ACTIVE),
m_laborCost(laborCost) m_laborCost(laborCost)
{ {
int numberOfInventoryItems = m_requiredInventoryItems.getSize(); int numberOfInventoryItems = m_requiredInventoryItems.getSize();
auto inventoryItemPointers = m_requiredInventoryItems.getValues(); auto inventoryItemPointers = m_requiredInventoryItems.getValues();
@@ -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();
} }
@@ -24,7 +24,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 +46,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 +65,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 +90,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);
@@ -208,7 +205,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 +224,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 +244,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 +293,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 +306,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 +336,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 +349,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 +385,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 +400,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 +412,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 +448,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 +467,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 +478,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 +494,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 +506,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 +545,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 +593,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 +603,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 +620,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 +646,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 +664,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 +679,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 +696,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 +706,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 +744,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 +797,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 +811,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 +821,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 +846,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();
} }
@@ -180,6 +180,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