Merged PR 1171: Service Management Refactoring -1927

User Story #1955
User Story #1956

**Changes**
* Added `DataStoreLockGuard` class to provide scoped automatic locking and unlocking for the datastore.
* Refactored `Service`, `ServiceBooking`, `JobCard`, and `ComboPackage` models to replace CSV-based serialization with fixed-size `Serialized*` struct records.
* Removed legacy `serialize()`, `deserialize()`, and `getHeaders()` methods from all affected model classes.
* Updated `DataStore` getter methods (`getServices`, `getComboPackages`, `getServiceBookings`, `getJobCards`) to load records from shared memory and automatically enrich them with linked entities (inventory items, services, bookings, users).
* Implemented generic save helpers (`saveServices`, `saveComboPackages`, etc.) using the `saveRecords` template to persist tracked records directly.
* Integrated `DataStoreLockGuard` into critical `ServiceManagementService` methods including `purchaseService`, `purchaseComboPackage`, `createService`, `removeService`, `cancelCustomerServiceBookings`, and `updateJobStatus`.
* Refactored cancellation workflows (`cancelCustomerServiceBookings`, `cancelTechnicianJobs`) to use `TrackedRecord` objects, correctly marking states as `MODIFIED` before persisting changes.
* Updated inventory restoration logic to accept tracked inventory maps and increment quantities while updating record states.
* Modified service layer access patterns to extract `.data` pointers from `TrackedRecord` wrappers instead of accessing raw map values directly.
* Added necessary header dependencies (`DataStoreLockGuard.h`, `SerializedRecords.h`) across data store and service layers.
* Removed redundant manual persistence calls in the service layer, relying on explicit `save*` calls after modifications within locked scopes.

Related work items: #1927, #1955, #1956
This commit is contained in:
Jissin Mathew
2026-06-15 15:24:40 +05:30
committed by Joel Thomas
11 changed files with 390 additions and 316 deletions
@@ -12,6 +12,8 @@ Date: 19-May-2026
#include "Config.h"
#include "SerializedRecords.h"
#include "FileHelper.h"
#include "ServiceBooking.h"
#include "JobCard.h"
/*
Function: DataStore
@@ -252,6 +254,28 @@ Returns:
*/
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;
}
@@ -265,6 +289,28 @@ Returns:
*/
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;
}
@@ -291,6 +337,49 @@ Returns:
*/
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;
}
@@ -304,9 +393,51 @@ Returns:
*/
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;
}
/*
Function: getInvoices
Description: Retrieves all invoice records from the datastore.
@@ -426,6 +557,7 @@ Returns:
*/
void DataStore::saveServices()
{
saveRecords<Service, SerializedService>(m_services, m_serviceCache);
}
/*
@@ -438,6 +570,7 @@ Returns:
*/
void DataStore::saveComboPackages()
{
saveRecords<ComboPackage, SerializedComboPackage>(m_comboPackages, m_comboPackageCache);
}
/*
@@ -462,6 +595,7 @@ Returns:
*/
void DataStore::saveServiceBookings()
{
saveRecords<ServiceBooking, SerializedServiceBooking>(m_serviceBookings, m_serviceBookingCache);
}
/*
@@ -474,6 +608,7 @@ Returns:
*/
void DataStore::saveJobCards()
{
saveRecords<JobCard, SerializedJobCard>(m_jobCards, m_jobCardCache);
}
/*
@@ -11,6 +11,7 @@ Date: 19-May-2026
#include "Map.h"
#include "MappingInfo.h"
#include "TrackedRecord.h"
#include "SerializedRecords.h"
#include "SharedMemory.h"
#include "User.h"
#include "Notification.h"
@@ -9,6 +9,7 @@ Date: 19-May-2026
#include <sstream>
#include <stdexcept>
#include "SerializedRecords.h"
#include "ComboPackage.h"
#include "Service.h"
#include "Factory.h"
@@ -28,7 +29,8 @@ Returns:
ComboPackage::ComboPackage()
: m_id("CMP" + std::to_string(++m_uid)),
m_status(util::State::ACTIVE),
m_discountPercentage(0.0) {}
m_discountPercentage(0.0) {
}
/*
Function: ComboPackage
@@ -270,72 +272,38 @@ static util::Vector<std::string> getServiceIDsAsVector(const std::string& servic
/*
Function: serialize
Description: Serializes the combo package into a CSV-formatted string.
Description: Serializes the ComboPackage object into a SerializedComboPackage record.
Parameters:
- None
Returns:
- std::string: Serialized combo package record
- SerializedComboPackage: Serialized representation of the combo package
*/
std::string ComboPackage::serialize() const
SerializedComboPackage ComboPackage::serialize() const
{
std::ostringstream serializedComboPackage;
serializedComboPackage << m_id << ','
<< m_packageName << ','
<< m_discountPercentage << ','
<< getServiceIDsAsString(m_serviceIDs) << ','
<< util::getStateString(m_status);
return serializedComboPackage.str();
SerializedComboPackage serialized = {};
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str());
strcpy_s(serialized.packageName, sizeof(serialized.packageName), m_packageName.c_str());
strcpy_s(serialized.serviceIDs, sizeof(serialized.serviceIDs), getServiceIDsAsString(m_serviceIDs).c_str());
serialized.discountPercentage = m_discountPercentage;
serialized.status = m_status;
return serialized;
}
/*
Function: deserialize
Description: Deserializes a CSV-formatted string into a ComboPackage object.
Description: Deserializes a SerializedComboPackage record into a ComboPackage object.
Parameters:
- record: const std::string&, serialized combo package record
- serializedComboPackage: const SerializedComboPackage&, serialized combo package record
Returns:
- ComboPackage*: Pointer to the deserialized ComboPackage object
Throws:
- std::runtime_error if data is invalid
*/
ComboPackage* ComboPackage::deserialize(const std::string& record)
ComboPackage* ComboPackage::deserialize(const SerializedComboPackage& serializedComboPackage)
{
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);
util::Vector<std::string> serviceIDs = getServiceIDsAsVector(serializedComboPackage.serviceIDs);
return Factory::getObject<ComboPackage>(
id,
packageName,
discountPercentage,
serializedComboPackage.id,
serializedComboPackage.packageName,
serializedComboPackage.discountPercentage,
serviceIDs,
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";
serializedComboPackage.status);
}
@@ -39,7 +39,6 @@ public:
void setDiscountPercentage(double discountPercentage);
void setServices(const util::Map<std::string, Service*>& services);
void setState(util::State status);
std::string serialize() const;
static ComboPackage* deserialize(const std::string&);
static std::string getHeaders();
SerializedComboPackage serialize() const;
static ComboPackage* deserialize(const SerializedComboPackage&);
};
@@ -9,6 +9,7 @@ Date:19-May-2026
#include <sstream>
#include <stdexcept>
#include "SerializedRecords.h"
#include "JobCard.h"
#include "Factory.h"
#include "StringHelper.h"
@@ -28,7 +29,8 @@ JobCard::JobCard()
m_booking(nullptr),
m_service(nullptr),
m_technician(nullptr),
m_status(util::ServiceJobStatus()) {}
m_status(util::ServiceJobStatus()) {
}
/*
Function: JobCard
@@ -65,7 +67,8 @@ JobCard::JobCard(const std::string& bookingId,
m_technician(technician),
m_assignedDate(assignedDate),
m_status(status),
m_completionDate(completionDate) {}
m_completionDate(completionDate) {
}
/*
Function: JobCard (parameterized constructor with ID)
@@ -351,79 +354,41 @@ void JobCard::setCompletionDate(const util::Timestamp& completionDate)
/*
Function: serialize
Description: Serializes the job card into a CSV-formatted string.
Description: Serializes the JobCard object into a SerializedJobCard record.
Parameters:
- None
Returns:
- std::string: Serialized job card record
- SerializedJobCard: Serialized representation of the job card
*/
std::string JobCard::serialize() const
SerializedJobCard JobCard::serialize() const
{
std::ostringstream serializedJobCard;
serializedJobCard << m_id << ','
<< m_bookingId << ','
<< m_serviceId << ','
<< m_technicianId << ','
<< m_assignedDate.toString() << ','
<< util::getServiceJobStatusString(m_status) << ','
<< m_completionDate.toString();
return serializedJobCard.str();
SerializedJobCard serialized = {};
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str());
strcpy_s(serialized.bookingId, sizeof(serialized.bookingId), m_bookingId.c_str());
strcpy_s(serialized.serviceId, sizeof(serialized.serviceId), m_serviceId.c_str());
strcpy_s(serialized.technicianId, sizeof(serialized.technicianId), m_technicianId.c_str());
serialized.assignedDate = m_assignedDate;
serialized.status = m_status;
serialized.completionDate = m_completionDate;
return serialized;
}
/*
Function: deserialize
Description: Deserializes a CSV-formatted string into a JobCard object.
Description: Deserializes a SerializedJobCard record into a JobCard object.
Parameters:
- record: const std::string&, serialized job card record
- serializedJobCard: const SerializedJobCard&, serialized job card record
Returns:
- JobCard*: Pointer to the deserialized JobCard object
Throws:
- std::runtime_error if timestamp parsing fails
*/
JobCard* JobCard::deserialize(const std::string& record)
JobCard* JobCard::deserialize(const SerializedJobCard& serializedJobCard)
{
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>(
id,
bookingId,
serviceId,
technicianId,
assignedDate,
status,
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";
serializedJobCard.id,
serializedJobCard.bookingId,
serializedJobCard.serviceId,
serializedJobCard.technicianId,
serializedJobCard.assignedDate,
serializedJobCard.status,
serializedJobCard.completionDate);
}
@@ -15,6 +15,7 @@ Date:19-May-2026
class ServiceBooking;
class Service;
class User;
struct SerializedJobCard;
class JobCard
{
@@ -70,7 +71,6 @@ public:
void setAssignedDate(const util::Timestamp& assignedDate);
void setStatus(util::ServiceJobStatus status);
void setCompletionDate(const util::Timestamp& completionDate);
std::string serialize() const;
static JobCard* deserialize(const std::string&);
static std::string getHeaders();
SerializedJobCard serialize() const;
static JobCard* deserialize(const SerializedJobCard&);
};
@@ -8,6 +8,7 @@ Date: 19-May-2026
*/
#include <sstream>
#include "SerializedRecords.h"
#include "Service.h"
#include "InventoryItem.h"
#include "StringHelper.h"
@@ -27,7 +28,8 @@ Returns:
Service::Service()
: m_id("SRV" + std::to_string(++m_uid)),
m_status(util::State::ACTIVE),
m_laborCost(0.0) {}
m_laborCost(0.0) {
}
/*
Function: Service
@@ -266,72 +268,38 @@ static util::Vector<std::string> getInventoryItemIDsAsVector(const std::string&
/*
Function: serialize
Description: Serializes the service into a CSV-formatted string.
Description: Serializes the Service object into a SerializedService record.
Parameters:
- None
Returns:
- std::string: Serialized service record
- SerializedService: Serialized representation of the service
*/
std::string Service::serialize() const
SerializedService Service::serialize() const
{
std::ostringstream serializedService;
serializedService << m_id << ','
<< m_name << ','
<< getInventoryItemIDsAsString(m_requiredInventoryItemIDs) << ','
<< m_laborCost << ','
<< util::getStateString(m_status);
return serializedService.str();
SerializedService serialized = {};
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str());
strcpy_s(serialized.name, sizeof(serialized.name), m_name.c_str());
strcpy_s(serialized.inventoryItemIDs, sizeof(serialized.inventoryItemIDs), getInventoryItemIDsAsString(m_requiredInventoryItemIDs).c_str());
serialized.laborCost = m_laborCost;
serialized.status = m_status;
return serialized;
}
/*
Function: deserialize
Description: Deserializes a CSV-formatted string into a Service object.
Description: Deserializes a SerializedService record into a Service object.
Parameters:
- record: const std::string&, serialized service record
- serializedService: const SerializedService&, serialized service record
Returns:
- Service*: Pointer to the deserialized Service object
Throws:
- std::runtime_error if labor cost parsing fails
*/
Service* Service::deserialize(const std::string& record)
Service* Service::deserialize(const SerializedService& serializedService)
{
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);
util::Vector<std::string> inventoryItemIDs = getInventoryItemIDsAsVector(serializedService.inventoryItemIDs);
return Factory::getObject<Service>(
id,
name,
serializedService.id,
serializedService.name,
inventoryItemIDs,
laborCost,
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";
serializedService.laborCost,
serializedService.status);
}
@@ -6,7 +6,6 @@ Author: Trenser
Date: 19-May-2026
*/
#pragma once
#include <string>
#include "Map.h"
@@ -14,6 +13,7 @@ Date: 19-May-2026
#include "Enums.h"
class InventoryItem;
struct SerializedService;
class Service
{
@@ -40,7 +40,6 @@ public:
void setRequiredInventoryItems(const util::Map<std::string, InventoryItem*>& requiredInventoryItems);
void setLaborCost(double laborCost);
void setState(util::State status);
std::string serialize() const;
static Service* deserialize(const std::string&);
static std::string getHeaders();
SerializedService serialize() const;
static Service* deserialize(const SerializedService&);
};
@@ -6,8 +6,10 @@ Description: Implementation file containing the method definitions of the
Author: Trenser
Date:19-May-2026
*/
#include <stdexcept>
#include <sstream>
#include "SerializedRecords.h"
#include "ServiceBooking.h"
#include "Service.h"
#include "Enums.h"
@@ -28,7 +30,8 @@ ServiceBooking::ServiceBooking()
m_customer(nullptr),
m_assignedTechnician(nullptr),
m_status(util::ServiceJobStatus::PENDING),
m_discountPercentage(0.0) {}
m_discountPercentage(0.0) {
}
/*
Function: ServiceBooking
@@ -437,84 +440,46 @@ static util::Vector<std::string> getServiceIDsAsVector(const std::string& servic
/*
Function: serialize
Description: Serializes the service booking into a CSV-formatted string.
Description: Serializes the ServiceBooking object into a SerializedServiceBooking record.
Parameters:
- None
Returns:
- std::string: Serialized booking record
- SerializedServiceBooking: Serialized representation of the service booking
*/
std::string ServiceBooking::serialize() const
SerializedServiceBooking ServiceBooking::serialize() const
{
std::ostringstream serializedBooking;
serializedBooking << m_id << ','
<< util::getServiceJobStatusString(m_status) << ','
<< getServiceIDsAsString(m_serviceIDs) << ','
<< m_customerId << ','
<< m_vehicleNumber << ','
<< m_vehicleBrand << ','
<< m_vehicleModel << ','
<< m_assignedTechnicianId << ','
<< m_discountPercentage << ',';
return serializedBooking.str();
SerializedServiceBooking serialized = {};
strcpy_s(serialized.id, sizeof(serialized.id), m_id.c_str());
strcpy_s(serialized.serviceIDs, sizeof(serialized.serviceIDs), getServiceIDsAsString(m_serviceIDs).c_str());
strcpy_s(serialized.customerId, sizeof(serialized.customerId), m_customerId.c_str());
strcpy_s(serialized.vehicleNumber, sizeof(serialized.vehicleNumber), m_vehicleNumber.c_str());
strcpy_s(serialized.vehicleBrand, sizeof(serialized.vehicleBrand), m_vehicleBrand.c_str());
strcpy_s(serialized.vehicleModel, sizeof(serialized.vehicleModel), m_vehicleModel.c_str());
strcpy_s(serialized.assignedTechnicianId, sizeof(serialized.assignedTechnicianId), m_assignedTechnicianId.c_str());
serialized.status = m_status;
serialized.discountPercentage = m_discountPercentage;
return serialized;
}
/*
Function: deserialize
Description: Deserializes a CSV-formatted string into a ServiceBooking object.
Description: Deserializes a SerializedServiceBooking record into a ServiceBooking object.
Parameters:
- record: const std::string&, serialized booking record
- serializedServiceBooking: const SerializedServiceBooking&, serialized service booking record
Returns:
- ServiceBooking*: Pointer to the deserialized ServiceBooking object
Throws:
- std::runtime_error if discount percentage parsing fails
*/
ServiceBooking* ServiceBooking::deserialize(const std::string& record)
ServiceBooking* ServiceBooking::deserialize(const SerializedServiceBooking& serializedServiceBooking)
{
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);
util::Vector<std::string> serviceIDs = getServiceIDsAsVector(serializedServiceBooking.serviceIDs);
return Factory::getObject<ServiceBooking>(
id,
status,
serializedServiceBooking.id,
serializedServiceBooking.status,
serviceIDs,
customerId,
vehicleNumber,
vehicleBrand,
vehicleModel,
assignedTechnicianId,
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";
serializedServiceBooking.customerId,
serializedServiceBooking.vehicleNumber,
serializedServiceBooking.vehicleBrand,
serializedServiceBooking.vehicleModel,
serializedServiceBooking.assignedTechnicianId,
serializedServiceBooking.discountPercentage);
}
@@ -6,6 +6,7 @@ Description: Header file declaring the ServiceBooking class, which represents
Author: Trenser
Date:19-May-2026
*/
#pragma once
#include <string>
#include "Map.h"
@@ -14,6 +15,7 @@ Date:19-May-2026
class Service;
class User;
struct SerializedServiceBooking;
class ServiceBooking
{
@@ -78,7 +80,6 @@ public:
void setAssignedTechnicianId(const std::string& assignedTechnicianId);
void setAssignedTechnician(User* assignedTechnician);
void setDiscountPercentage(double discountPercentage);
std::string serialize() const;
static ServiceBooking* deserialize(const std::string&);
static std::string getHeaders();
SerializedServiceBooking serialize() const;
static ServiceBooking* deserialize(const SerializedServiceBooking&);
};
@@ -24,6 +24,7 @@ Date:19-May-2026
#include "Timestamp.h"
#include "User.h"
#include "UserManagementService.h"
#include "DataStoreLockGuard.h"
#include "Utility.h"
#include "DataStoreLockGuard.h"
@@ -46,18 +47,19 @@ void ServiceManagementService::purchaseService(const util::Vector<std::string>&
{
throw std::runtime_error("No user is currently logged in!");
}
auto& servicesMap = m_dataStore.getServices();
auto& serviceBookingMap = m_dataStore.getServiceBookings();
DataStoreLockGuard lock(m_dataStore);
auto& trackedServicesMap = m_dataStore.getServices();
auto& trackedServiceBookingMap = m_dataStore.getServiceBookings();
util::Map<std::string, Service*> selectedServices;
int selectedServicesCount = serviceIDs.getSize();
for (int index = 0; index < selectedServicesCount; index++)
{
int serviceIndex = servicesMap.find(serviceIDs[index]);
int serviceIndex = trackedServicesMap.find(serviceIDs[index]);
if (serviceIndex == -1)
{
throw std::runtime_error("Service not found!");
}
Service* service = servicesMap.getValueAt(serviceIndex);
Service* service = trackedServicesMap.getValueAt(serviceIndex).data;
selectedServices[service->getId()] = service;
}
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0);
@@ -65,7 +67,7 @@ void ServiceManagementService::purchaseService(const util::Vector<std::string>&
{
throw std::runtime_error("Failed to create service booking");
}
serviceBookingMap[serviceBooking->getId()] = serviceBooking;
trackedServiceBookingMap[serviceBooking->getId()] = util::createNewRecord(serviceBooking);
std::string title = "Service Booking succeeded";
std::string message = "Your service booking has been successfully placed with ID " + serviceBooking->getId();
sendNotification(authenticatedUser, title, message);
@@ -90,21 +92,22 @@ void ServiceManagementService::purchaseComboPackage(const std::string& comboPack
{
throw std::runtime_error("No user is currently logged in!");
}
auto& comboPackagesMap = m_dataStore.getComboPackages();
auto& serviceBookingMap = m_dataStore.getServiceBookings();
int comboPackageIndex = comboPackagesMap.find(comboPackageID);
DataStoreLockGuard lock(m_dataStore);
auto& trackedComboPackagesMap = m_dataStore.getComboPackages();
auto& trackedServiceBookingMap = m_dataStore.getServiceBookings();
int comboPackageIndex = trackedComboPackagesMap.find(comboPackageID);
if (comboPackageIndex == -1)
{
throw std::runtime_error("Combo Package not found!");
}
const ComboPackage* comboPackage = comboPackagesMap[comboPackageID];
const ComboPackage* comboPackage = trackedComboPackagesMap[comboPackageID].data;
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());
if (serviceBooking == nullptr)
{
throw std::runtime_error("Failed to create combo package service booking");
}
serviceBookingMap[serviceBooking->getId()] = serviceBooking;
trackedServiceBookingMap[serviceBooking->getId()] = util::createNewRecord(serviceBooking);
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();
sendNotification(authenticatedUser, title, message);
@@ -205,7 +208,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
Return type: void
*/
static void restoreInventory(ServiceBooking* booking)
static void restoreInventory(ServiceBooking* booking, util::Map<std::string, TrackedRecord<InventoryItem>>& trackedInventoryItems)
{
const int INCREMENT_VALUE = 1;
if (!booking)
@@ -224,9 +227,17 @@ static void restoreInventory(ServiceBooking* booking)
for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++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)
{
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
currentTrackedInventoryItem.state = RecordState::MODIFIED;
}
}
}
@@ -244,23 +255,28 @@ Parameters:
util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN)
Return type: void
*/
static void processBookingCancellation(ServiceBooking* booking,
util::Map<std::string, JobCard*>& jobs,
static void processBookingCancellation(TrackedRecord<ServiceBooking>& trackedBooking,
util::Map<std::string, TrackedRecord<JobCard>>& jobs,
ServiceManagementService& currentService,
util::UserType userType)
util::UserType userType,
util::Map<std::string, TrackedRecord<InventoryItem>>& trackedInventoryItems)
{
ServiceBooking* booking = trackedBooking.data;
if (!booking)
{
return;
}
const std::string& bookingId = booking->getId();
for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator)
{
JobCard* jobCard = jobs.getValueAt(jobIterator);
auto& trackedJobCard = jobs.getValueAt(jobIterator);
JobCard* jobCard = trackedJobCard.data;
if (!jobCard || jobCard->getBookingId() != booking->getId() || jobCard->getStatus() == util::ServiceJobStatus::CANCELLED)
{
continue;
}
jobCard->setStatus(util::ServiceJobStatus::CANCELLED);
trackedJobCard.state = RecordState::MODIFIED;
if (userType == util::UserType::CUSTOMER)
{
if (User* technician = booking->getAssignedTechnician())
@@ -293,7 +309,8 @@ static void processBookingCancellation(ServiceBooking* booking,
}
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
restoreInventory(booking);
trackedBooking.state = RecordState::MODIFIED;
restoreInventory(booking, trackedInventoryItems);
}
/*
@@ -306,22 +323,25 @@ Return type: void
*/
void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID)
{
auto& users = m_dataStore.getUsers();
int userIndex = users.find(customerID);
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsers = m_dataStore.getUsers();
int userIndex = trackedUsers.find(customerID);
if (userIndex == -1)
{
throw std::runtime_error("User not found: " + customerID);
}
User* customer = users.getValueAt(userIndex);
User* customer = trackedUsers.getValueAt(userIndex).data;
if (!customer)
{
throw std::runtime_error("User not found: " + customerID);
}
auto& bookings = m_dataStore.getServiceBookings();
auto& jobs = m_dataStore.getJobCards();
for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
auto& trackedBookings = m_dataStore.getServiceBookings();
auto& trackedJobs = m_dataStore.getJobCards();
auto& trackedInventoryItems = m_dataStore.getInventoryItems();
for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++)
{
ServiceBooking* booking = bookings.getValueAt(iteratorOne);
auto& trackedBooking = trackedBookings.getValueAt(iteratorOne);
ServiceBooking* booking = trackedBooking.data;
if (!booking)
{
continue;
@@ -336,8 +356,12 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string&
{
continue;
}
processBookingCancellation(booking, jobs, *this, util::UserType::CUSTOMER);
processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::CUSTOMER, trackedInventoryItems);
}
m_dataStore.saveUsers();
m_dataStore.saveServiceBookings();
m_dataStore.saveJobCards();
m_dataStore.saveInventoryItems();
}
/*
@@ -349,22 +373,25 @@ Return type: void
*/
void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID)
{
auto& users = m_dataStore.getUsers();
int userIndex = users.find(technicianID);
DataStoreLockGuard lock(m_dataStore);
auto& trackedUsers = m_dataStore.getUsers();
int userIndex = trackedUsers.find(technicianID);
if (userIndex == -1)
{
throw std::runtime_error("User not found: " + technicianID);
}
User* technician = users.getValueAt(userIndex);
User* technician = trackedUsers.getValueAt(userIndex).data;
if (!technician)
{
throw std::runtime_error("User not found: " + technicianID);
}
auto& bookings = m_dataStore.getServiceBookings();
auto& jobs = m_dataStore.getJobCards();
for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
auto& trackedBookings = m_dataStore.getServiceBookings();
auto& trackedJobs = m_dataStore.getJobCards();
auto& trackedInventoryItems = m_dataStore.getInventoryItems();
for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++)
{
ServiceBooking* booking = bookings.getValueAt(iteratorOne);
auto& trackedBooking = trackedBookings.getValueAt(iteratorOne);
ServiceBooking* booking = trackedBooking.data;
if (!booking)
{
continue;
@@ -385,8 +412,12 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia
{
continue;
}
processBookingCancellation(booking, jobs, *this, util::UserType::TECHNICIAN);
processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::TECHNICIAN, trackedInventoryItems);
}
m_dataStore.saveUsers();
m_dataStore.saveInventoryItems();
m_dataStore.saveServiceBookings();
m_dataStore.saveJobCards();
}
/*
@@ -400,6 +431,7 @@ Return type: void
*/
void ServiceManagementService::createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDsInNewCombo, double discountPercentage)
{
DataStoreLockGuard lock(m_dataStore);
if (packageName.empty())
{
throw std::invalid_argument("The Combo Package Name cannot be empty.\n");
@@ -412,19 +444,19 @@ void ServiceManagementService::createComboPackage(const std::string& packageName
{
throw std::invalid_argument("Discount percentage must be between 0 and 100.");
}
auto& servicesMap = m_dataStore.getServices();
auto& trackedServicesMap = m_dataStore.getServices();
for (int index = 0; index < serviceIDsInNewCombo.getSize(); index++)
{
const std::string serviceid = serviceIDsInNewCombo[index];
if (servicesMap.find(serviceid) == -1)
const std::string& serviceid = serviceIDsInNewCombo[index];
if (trackedServicesMap.find(serviceid) == -1)
{
throw std::runtime_error("Service ID not found: " + serviceid);
}
}
auto& comboPackageMap = m_dataStore.getComboPackages();
for (int iterator = 0; iterator < comboPackageMap.getSize(); iterator++)
auto& trackedComboPackageMap = m_dataStore.getComboPackages();
for (int iterator = 0; iterator < trackedComboPackageMap.getSize(); iterator++)
{
ComboPackage* existingCombos = comboPackageMap.getValueAt(iterator);
ComboPackage* existingCombos = trackedComboPackageMap.getValueAt(iterator).data;
const util::Map<std::string, Service*>& servicesInsideExistingCombos = existingCombos->getServices();
if (servicesInsideExistingCombos.getSize() == serviceIDsInNewCombo.getSize())
{
@@ -448,15 +480,16 @@ void ServiceManagementService::createComboPackage(const std::string& packageName
for (int iteratorOne = 0; iteratorOne < serviceIDsInNewCombo.getSize(); iteratorOne++)
{
const std::string& serviceId = serviceIDsInNewCombo[iteratorOne];
int serviceIndex = servicesMap.find(serviceId);
int serviceIndex = trackedServicesMap.find(serviceId);
if (serviceIndex == -1)
{
throw std::runtime_error("Service ID not found: " + serviceId);
}
selectedServices.insert(serviceId, servicesMap.getValueAt(serviceIndex));
selectedServices.insert(serviceId, trackedServicesMap.getValueAt(serviceIndex).data);
}
ComboPackage* newComboPackage = Factory::getObject<ComboPackage>(packageName, discountPercentage, selectedServices);
comboPackageMap.insert(newComboPackage->getId(), newComboPackage);
trackedComboPackageMap.insert(newComboPackage->getId(), util::createNewRecord(newComboPackage));
m_dataStore.saveComboPackages();
}
/*
@@ -467,7 +500,10 @@ Return type: util::Map<std::string, ComboPackage*>
*/
util::Map<std::string, ComboPackage*> ServiceManagementService::getComboPackages()
{
return m_dataStore.getComboPackages();
DataStoreLockGuard lock(m_dataStore);
util::Map<std::string, ComboPackage*> comboPackages;
comboPackages = util::getObjects(m_dataStore.getComboPackages());
return comboPackages;
}
/*
@@ -478,14 +514,17 @@ Return type: void
*/
void ServiceManagementService::removeComboPackage(const std::string& comboPackageID)
{
DataStoreLockGuard lock(m_dataStore);
bool removed = false;
util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
auto& trackedComboPackages = m_dataStore.getComboPackages();
for (int iterator = 0; iterator < trackedComboPackages.getSize(); iterator++)
{
ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
auto& comboPackage = trackedComboPackages.getValueAt(iterator);
ComboPackage* currentComboPackage = comboPackage.data;
if (currentComboPackage && currentComboPackage->getId() == comboPackageID)
{
currentComboPackage->setState(util::State::INACTIVE);
comboPackage.state = RecordState::MODIFIED;
removed = true;
break;
}
@@ -494,6 +533,7 @@ void ServiceManagementService::removeComboPackage(const std::string& comboPackag
{
throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found.");
}
m_dataStore.saveComboPackages();
}
/*
@@ -506,7 +546,10 @@ Returns:
*/
util::Map<std::string, ServiceBooking*> ServiceManagementService::getServiceBookings()
{
return m_dataStore.getServiceBookings();
DataStoreLockGuard lock(m_dataStore);
util::Map<std::string, ServiceBooking*> serviceBookings;
serviceBookings = util::getObjects(m_dataStore.getServiceBookings());
return serviceBookings;
}
/*
@@ -545,9 +588,10 @@ Throws:
*/
void ServiceManagementService::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID)
{
DataStoreLockGuard lock(m_dataStore);
UserManagementService m_userManagementService;
ServiceBooking* currentBooking = getServiceBooking(bookingID);
auto& currentJobCards = m_dataStore.getJobCards();
auto& currentTrackedJobCards = m_dataStore.getJobCards();
if (currentBooking == nullptr)
{
throw std::runtime_error("Service Booking not available");
@@ -593,7 +637,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());
if (jobCard)
{
currentJobCards.insert(jobCard->getId(), jobCard);
currentTrackedJobCards.insert(jobCard->getId(), util::createNewRecord(jobCard));
sendNotification(selectedTechnician, title, message);
}
else
@@ -603,6 +647,7 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
title = "Technician assigned";
message = "A technician has been assigned to your Service Booking with ID " + bookingID;
sendNotification(currentBooking->getCustomer(), title, message);
m_dataStore.saveJobCards();
}
/*
@@ -620,15 +665,17 @@ Throws:
*/
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;
auto inventoryItems = m_dataStore.getInventoryItems();
auto& trackedInventoryItems = m_dataStore.getInventoryItems();
auto& currentServices = m_dataStore.getServices();
for (int iteratorOne =0; iteratorOne < inventoryItemIDs.getSize(); iteratorOne++)
{
std::string currentItemID = inventoryItemIDs[iteratorOne];
bool itemFound = false;
for (int iteratorTwo = 0; iteratorTwo < inventoryItems.getSize(); iteratorTwo++)
for (int iteratorTwo = 0; iteratorTwo < trackedInventoryItems.getSize(); iteratorTwo++)
{
InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iteratorTwo);
InventoryItem* currentInventoryItem = trackedInventoryItems.getValueAt(iteratorTwo).data;
if (currentInventoryItem && currentInventoryItem->getId() == currentItemID)
{
itemFound = true;
@@ -646,12 +693,12 @@ void ServiceManagementService::createService(const std::string& name, const util
{
throw std::runtime_error("Unable to create new service.");
}
util::Map<std::string, Service*>& currentServices = m_dataStore.getServices();
if (currentServices.find(newService->getId()) != -1)
{
throw std::runtime_error("Service with this ID Already exists.");
}
currentServices.insert(newService->getId(), newService);
currentServices.insert(newService->getId(), util::createNewRecord(newService));
m_dataStore.saveServices();
}
/*
@@ -664,7 +711,10 @@ Returns:
*/
util::Map<std::string, Service*> ServiceManagementService::getServices()
{
return m_dataStore.getServices();
DataStoreLockGuard lock(m_dataStore);
util::Map<std::string, Service*> services;
services = util::getObjects(m_dataStore.getServices());
return services;
}
/*
@@ -679,14 +729,19 @@ Throws:
*/
void ServiceManagementService::removeService(const std::string& serviceID)
{
util::Map<std::string, Service*>& currentServices = m_dataStore.getServices();
util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
if (currentServices.find(serviceID) != -1)
DataStoreLockGuard lock(m_dataStore);
auto& currentTrackedServices = m_dataStore.getServices();
auto& currentTrackedComboPackages = m_dataStore.getComboPackages();
if (currentTrackedServices.find(serviceID) != -1)
{
currentServices.getValueAt(currentServices.find(serviceID))->setState(util::State::INACTIVE);
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
int serviceIndex, comboPackageIndex;
serviceIndex = currentTrackedServices.find(serviceID);
currentTrackedServices.getValueAt(serviceIndex).data->setState(util::State::INACTIVE);
currentTrackedServices.getValueAt(serviceIndex).state = RecordState::MODIFIED;
for (int iterator = 0; iterator < currentTrackedComboPackages.getSize(); iterator++)
{
ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
comboPackageIndex = iterator;
ComboPackage* currentComboPackage = currentTrackedComboPackages.getValueAt(iterator).data;
if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE)
{
util::Map<std::string, Service*> currentServices = currentComboPackage->getServices();
@@ -696,6 +751,7 @@ void ServiceManagementService::removeService(const std::string& serviceID)
if (currentService->getId() == serviceID)
{
currentComboPackage->setState(util::State::INACTIVE);
currentTrackedComboPackages.getValueAt(comboPackageIndex).state = RecordState::MODIFIED;
break;
}
}
@@ -706,6 +762,8 @@ void ServiceManagementService::removeService(const std::string& serviceID)
{
throw std::runtime_error("Service not found.");
}
m_dataStore.saveServices();
m_dataStore.saveComboPackages();
}
/*
@@ -744,11 +802,12 @@ Returns:
*/
util::Map<std::string, JobCard*> ServiceManagementService::getJobCards(const std::string& technicianID)
{
util::Map<std::string, JobCard*> jobCards = m_dataStore.getJobCards();
DataStoreLockGuard lock(m_dataStore);
auto& trackedJobCards = m_dataStore.getJobCards();
util::Map<std::string, JobCard*> technicianJobCards;
for (int iterator = 0; iterator < jobCards.getSize(); iterator++)
for (int iterator = 0; iterator < trackedJobCards.getSize(); iterator++)
{
JobCard* currentJobCard = jobCards.getValueAt(iterator);
JobCard* currentJobCard = trackedJobCards.getValueAt(iterator).data;
if (currentJobCard->getTechnicianId() == technicianID)
{
technicianJobCards.insert(currentJobCard->getId(), currentJobCard);
@@ -797,6 +856,7 @@ Returns:
*/
void ServiceManagementService::updateJobStatus(const std::string& jobID)
{
DataStoreLockGuard lock(m_dataStore);
AuthenticationManagementService authenticationManagementService;
PaymentManagementService paymentManagementService;
bool jobStatusUpdated = false, serviceBookingCompleted;
@@ -811,8 +871,15 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
{
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)
{
int jobIndex = trackedJobCards.find(jobID);
if (jobIndex == -1)
{
throw std::runtime_error("Unable to fetch current job.");
}
currentJob = currentAssignedJobs.getValueAt(currentAssignedJobs.find(jobID));
if (currentJob == nullptr)
{
@@ -821,16 +888,20 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED)
{
currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS);
trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED;
jobStatusUpdated = true;
}
else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS)
{
currentJob->setStatus(util::ServiceJobStatus::COMPLETED);
trackedJobCards.getValueAt(jobIndex).state = RecordState::MODIFIED;
jobStatusUpdated = true;
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
if (serviceBookingCompleted)
{
const std::string& bookingId = currentJob->getBookingId();
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
trackedServiceBookings.getValueAt(trackedServiceBookings.find(bookingId)).state = RecordState::MODIFIED;
paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated.";
@@ -846,4 +917,6 @@ void ServiceManagementService::updateJobStatus(const std::string& jobID)
{
throw std::runtime_error("Failed to update job status. Job may already be completed.");
}
m_dataStore.saveJobCards();
m_dataStore.saveServiceBookings();
}