1156 lines
42 KiB
C++
1156 lines
42 KiB
C++
/*
|
|
File: ServiceManagementService.cpp
|
|
Description: Implementation file containing the method definitions of the
|
|
ServiceManagementService class, including service and combo package
|
|
purchasing logic, booking creation, and notification handling.
|
|
Author: Trenser
|
|
Date:19-May-2026
|
|
*/
|
|
|
|
#include <stdexcept>
|
|
#include "AuthenticationManagementService.h"
|
|
#include "ComboPackage.h"
|
|
#include "Config.h"
|
|
#include "DataStore.h"
|
|
#include "Enums.h"
|
|
#include "Factory.h"
|
|
#include "FileManager.h"
|
|
#include "InventoryItem.h"
|
|
#include "JobCard.h"
|
|
#include "NotificationManagementService.h"
|
|
#include "PaymentManagementService.h"
|
|
#include "Service.h"
|
|
#include "ServiceBooking.h"
|
|
#include "ServiceManagementService.h"
|
|
#include "Timestamp.h"
|
|
#include "User.h"
|
|
#include "UserManagementService.h"
|
|
#include "Utility.h"
|
|
|
|
/*
|
|
Function: purchaseService
|
|
Description: Creates a new service booking for the authenticated user. Validates
|
|
service IDs, retrieves services from the DataStore, and generates a
|
|
booking. Sends a notification upon successful booking.
|
|
Parameter: const util::Vector<std::string>& serviceIDs - IDs of services to purchase
|
|
const std::string& vehicleNumber - vehicle registration number
|
|
const std::string& vehicleBrand - brand of the vehicle
|
|
const std::string& vehicleModel - model of the vehicle
|
|
Return type: void
|
|
*/
|
|
void ServiceManagementService::purchaseService(const util::Vector<std::string>& serviceIDs, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel)
|
|
{
|
|
AuthenticationManagementService m_authenticationManagementService;
|
|
auto authenticatedUser = m_authenticationManagementService.getAuthenticatedUser();
|
|
if (authenticatedUser == nullptr)
|
|
{
|
|
throw std::runtime_error("No user is currently logged in!");
|
|
}
|
|
auto& servicesMap = m_dataStore.getServices();
|
|
auto& serviceBookingMap = 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]);
|
|
if (serviceIndex == -1)
|
|
{
|
|
throw std::runtime_error("Service not found!");
|
|
}
|
|
Service* service = servicesMap.getValueAt(serviceIndex);
|
|
selectedServices[service->getId()] = service;
|
|
}
|
|
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0);
|
|
if (serviceBooking == nullptr)
|
|
{
|
|
throw std::runtime_error("Failed to create service booking");
|
|
}
|
|
serviceBookingMap[serviceBooking->getId()] = 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);
|
|
}
|
|
|
|
/*
|
|
Function: purchaseComboPackage
|
|
Description: Creates a new service booking for a combo package. Validates the combo
|
|
package ID, retrieves services from the package, and generates a booking
|
|
with the applicable discount. Sends a notification upon successful booking.
|
|
Parameter: const std::string& comboPackageID - ID of the combo package
|
|
const std::string& vehicleNumber - vehicle registration number
|
|
const std::string& vehicleBrand - brand of the vehicle
|
|
const std::string& vehicleModel - model of the vehicle
|
|
Return type: void
|
|
*/
|
|
void ServiceManagementService::purchaseComboPackage(const std::string& comboPackageID, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel)
|
|
{
|
|
AuthenticationManagementService m_authenticationManagementService;
|
|
auto authenticatedUser = m_authenticationManagementService.getAuthenticatedUser();
|
|
if (authenticatedUser == nullptr)
|
|
{
|
|
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);
|
|
if (comboPackageIndex == -1)
|
|
{
|
|
throw std::runtime_error("Combo Package not found!");
|
|
}
|
|
const ComboPackage* comboPackage = comboPackagesMap[comboPackageID];
|
|
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;
|
|
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);
|
|
}
|
|
|
|
util::Map<std::string, User*> ServiceManagementService::m_observers{};
|
|
|
|
/*
|
|
Function: attach
|
|
Description: Attaches a user as an observer to the ServiceManagementService for receiving notifications.
|
|
Parameters:
|
|
- user: Pointer to the User object to be attached.
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::attach(User* user)
|
|
{
|
|
if (user)
|
|
{
|
|
const std::string& userID = user->getId();
|
|
if (m_observers.find(userID) == -1)
|
|
{
|
|
m_observers[userID] = user;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: detach
|
|
Description: Detaches a user from the observer list of the ServiceManagementService.
|
|
Parameters:
|
|
- user: Pointer to the User object to be detached.
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::detach(User* user)
|
|
{
|
|
if (user)
|
|
{
|
|
const std::string& userID = user->getId();
|
|
if (m_observers.find(userID) != -1)
|
|
{
|
|
m_observers.remove(userID);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: sendNotification
|
|
Description: Sends a notification to a user if they are registered as an observer.
|
|
Parameters:
|
|
- user: Pointer to the User object to receive the notification.
|
|
- title: Title of the notification.
|
|
- message: Message content of the notification.
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if notification creation fails.
|
|
*/
|
|
void ServiceManagementService::sendNotification(User* user, const std::string& title, const std::string& message)
|
|
{
|
|
if (user)
|
|
{
|
|
if (m_observers.find(user->getId()) != -1)
|
|
{
|
|
Notification* notification =
|
|
Factory::getObject<Notification>(
|
|
user->getId(),
|
|
user,
|
|
title,
|
|
message,
|
|
util::Timestamp()
|
|
);
|
|
if (notification)
|
|
{
|
|
user->addNotification(notification);
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Failed to create notification");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: getObserverIDs
|
|
Description: Retrieves the IDs of all observers currently attached to the
|
|
ServiceManagementService.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- util::Vector<std::string>: Vector of observer user IDs
|
|
*/
|
|
util::Vector<std::string> ServiceManagementService::getObserverIDs()
|
|
{
|
|
util::Vector<std::string> observerIDs;
|
|
int numberOfObservers = m_observers.getSize();
|
|
for (int index = 0; index < numberOfObservers; index++)
|
|
{
|
|
User* observer = m_observers.getValueAt(index);
|
|
if (observer)
|
|
{
|
|
observerIDs.push_back(observer->getId());
|
|
}
|
|
}
|
|
return observerIDs;
|
|
}
|
|
|
|
/*
|
|
Function: loadServices
|
|
Description: Loads services from persistent storage into the datastore.
|
|
Validates required inventory items and attaches them to each service.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if an inventory item ID is invalid
|
|
*/
|
|
void ServiceManagementService::loadServices()
|
|
{
|
|
util::FileManager<Service> serviceFileManager(config::file::SERVICE_FILE);
|
|
auto& services = m_dataStore.getServices();
|
|
auto& inventoryItems = m_dataStore.getInventoryItems();
|
|
auto servicesMap = serviceFileManager.load();
|
|
for (int serviceIndex = 0; serviceIndex < servicesMap.getSize(); serviceIndex++)
|
|
{
|
|
Service* service = servicesMap.getValueAt(serviceIndex);
|
|
services[service->getId()] = service;
|
|
util::Map<std::string, InventoryItem*> inventoryItemsMap;
|
|
auto& inventoryItemIDs = service->getRequiredInventoryItemIDs();
|
|
for (int inventoryItemIndex = 0; inventoryItemIndex < inventoryItemIDs.getSize(); inventoryItemIndex++)
|
|
{
|
|
const std::string& inventoryItemID = inventoryItemIDs[inventoryItemIndex];
|
|
int index = inventoryItems.find(inventoryItemID);
|
|
if (index == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Inventory Item ID");
|
|
}
|
|
inventoryItemsMap[inventoryItemID] = inventoryItems.getValueAt(index);
|
|
}
|
|
service->setRequiredInventoryItems(inventoryItemsMap);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: saveServices
|
|
Description: Saves services from the datastore to persistent storage.
|
|
Uses FileManager to serialize services into the configured file.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::saveServices()
|
|
{
|
|
util::FileManager<Service> serviceFileManager(config::file::SERVICE_FILE);
|
|
auto& services = m_dataStore.getServices();
|
|
serviceFileManager.save(services);
|
|
}
|
|
|
|
/*
|
|
Function: loadComboPackages
|
|
Description: Loads combo packages from persistent storage into the datastore.
|
|
Validates associated services and attaches them to each package.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if a service ID is invalid
|
|
*/
|
|
void ServiceManagementService::loadComboPackages()
|
|
{
|
|
util::FileManager<ComboPackage> comboPackageFileManager(config::file::COMBOPACKAGE_FILE);
|
|
auto& comboPackages = m_dataStore.getComboPackages();
|
|
auto& services = m_dataStore.getServices();
|
|
auto comboPackagesMap = comboPackageFileManager.load();
|
|
for (int packageIndex = 0; packageIndex < comboPackagesMap.getSize(); packageIndex++)
|
|
{
|
|
ComboPackage* comboPackage = comboPackagesMap.getValueAt(packageIndex);
|
|
util::Map<std::string, Service*> packageServices;
|
|
auto& serviceIDs = comboPackage->getServiceIDs();
|
|
for (int serviceIndex = 0; serviceIndex < serviceIDs.getSize(); serviceIndex++)
|
|
{
|
|
const std::string& serviceID = serviceIDs[serviceIndex];
|
|
int serviceMapIndex = services.find(serviceID);
|
|
if (serviceMapIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Service ID");
|
|
}
|
|
packageServices[serviceID] = services.getValueAt(serviceMapIndex);
|
|
}
|
|
comboPackage->setServices(packageServices);
|
|
comboPackages[comboPackage->getId()] = comboPackage;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: saveComboPackages
|
|
Description: Saves combo packages from the datastore to persistent storage.
|
|
Uses FileManager to serialize combo packages into the configured file.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::saveComboPackages()
|
|
{
|
|
util::FileManager<ComboPackage> comboPackageFileManager(config::file::COMBOPACKAGE_FILE);
|
|
auto& comboPackages = m_dataStore.getComboPackages();
|
|
comboPackageFileManager.save(comboPackages);
|
|
}
|
|
|
|
/*
|
|
Function: loadServiceBookings
|
|
Description: Loads service bookings from persistent storage into the datastore.
|
|
Validates associated services, customers, and technicians before
|
|
attaching them to each booking.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if a service ID, customer ID, or technician ID is invalid
|
|
- std::runtime_error if a user is not of the expected type (customer/technician)
|
|
*/
|
|
void ServiceManagementService::loadServiceBookings()
|
|
{
|
|
util::FileManager<ServiceBooking> bookingFileManager(config::file::SERVICEBOOKING_FILE);
|
|
auto& serviceBookings = m_dataStore.getServiceBookings();
|
|
auto& services = m_dataStore.getServices();
|
|
auto& users = m_dataStore.getUsers();
|
|
auto bookingsMap = bookingFileManager.load();
|
|
for (int bookingIndex = 0; bookingIndex < bookingsMap.getSize(); bookingIndex++)
|
|
{
|
|
ServiceBooking* booking = bookingsMap.getValueAt(bookingIndex);
|
|
util::Map<std::string, Service*> bookingServices;
|
|
auto& serviceIDs = booking->getServiceIDs();
|
|
for (int serviceIndex = 0; serviceIndex < serviceIDs.getSize(); serviceIndex++)
|
|
{
|
|
const std::string& serviceID = serviceIDs[serviceIndex];
|
|
int serviceMapIndex = services.find(serviceID);
|
|
if (serviceMapIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Service ID");
|
|
}
|
|
|
|
bookingServices[serviceID] = services.getValueAt(serviceMapIndex);
|
|
}
|
|
booking->setServices(bookingServices);
|
|
int customerIndex = users.find(booking->getCustomerId());
|
|
if (customerIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Customer ID");
|
|
}
|
|
User* customer = users.getValueAt(customerIndex);
|
|
if (customer->getUserType() != util::UserType::CUSTOMER)
|
|
{
|
|
throw std::runtime_error("User is not a customer");
|
|
}
|
|
booking->setCustomer(customer);
|
|
const std::string& technicianId = booking->getAssignedTechnicianId();
|
|
if (!technicianId.empty())
|
|
{
|
|
int technicianIndex = users.find(technicianId);
|
|
if (technicianIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Technician ID");
|
|
}
|
|
User* technician = users.getValueAt(technicianIndex);
|
|
if (technician->getUserType() != util::UserType::TECHNICIAN)
|
|
{
|
|
throw std::runtime_error("User is not a technician");
|
|
}
|
|
booking->setAssignedTechnician(technician);
|
|
}
|
|
serviceBookings[booking->getId()] = booking;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: saveServiceBookings
|
|
Description: Saves service bookings from the datastore to persistent storage.
|
|
Uses FileManager to serialize bookings into the configured file.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::saveServiceBookings()
|
|
{
|
|
util::FileManager<ServiceBooking> bookingFileManager(config::file::SERVICEBOOKING_FILE);
|
|
auto& serviceBookings = m_dataStore.getServiceBookings();
|
|
bookingFileManager.save(serviceBookings);
|
|
}
|
|
|
|
/*
|
|
Function: loadJobCards
|
|
Description: Loads job cards from persistent storage into the datastore.
|
|
Validates associated bookings, services, and technicians before
|
|
attaching them to each job card.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if a booking ID, service ID, or technician ID is invalid
|
|
- std::runtime_error if a service does not belong to the booking
|
|
- std::runtime_error if a user is not a technician
|
|
*/
|
|
void ServiceManagementService::loadJobCards()
|
|
{
|
|
util::FileManager<JobCard> jobCardFileManager(config::file::JOBCARD_FILE);
|
|
auto& jobCards = m_dataStore.getJobCards();
|
|
auto& serviceBookings = m_dataStore.getServiceBookings();
|
|
auto& services = m_dataStore.getServices();
|
|
auto& users = m_dataStore.getUsers();
|
|
auto jobCardsMap = jobCardFileManager.load();
|
|
for (int jobCardIndex = 0; jobCardIndex < jobCardsMap.getSize(); jobCardIndex++)
|
|
{
|
|
JobCard* jobCard = jobCardsMap.getValueAt(jobCardIndex);
|
|
int bookingIndex = serviceBookings.find(jobCard->getBookingId());
|
|
if (bookingIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Booking ID");
|
|
}
|
|
ServiceBooking* booking = serviceBookings.getValueAt(bookingIndex);
|
|
jobCard->setBooking(booking);
|
|
int serviceIndex = services.find(jobCard->getServiceId());
|
|
if (serviceIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Service ID");
|
|
}
|
|
Service* service = services.getValueAt(serviceIndex);
|
|
if (booking->getServices().find(jobCard->getServiceId()) == -1)
|
|
{
|
|
throw std::runtime_error("Service does not belong to booking");
|
|
}
|
|
jobCard->setService(service);
|
|
int technicianIndex = users.find(jobCard->getTechnicianId());
|
|
if (technicianIndex == -1)
|
|
{
|
|
throw std::runtime_error("Invalid Technician ID");
|
|
}
|
|
User* technician = users.getValueAt(technicianIndex);
|
|
if (technician->getUserType() != util::UserType::TECHNICIAN)
|
|
{
|
|
throw std::runtime_error("User is not a technician");
|
|
}
|
|
jobCard->setTechnician(technician);
|
|
jobCards[jobCard->getId()] = jobCard;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: saveJobCards
|
|
Description: Saves job cards from the datastore to persistent storage.
|
|
Uses FileManager to serialize job cards into the configured file.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::saveJobCards()
|
|
{
|
|
util::FileManager<JobCard> jobCardFileManager(config::file::JOBCARD_FILE);
|
|
auto& jobCards = m_dataStore.getJobCards();
|
|
jobCardFileManager.save(jobCards);
|
|
}
|
|
|
|
/*
|
|
Function: loadObservers
|
|
Description: Loads observer IDs from persistent storage and attaches corresponding
|
|
users as observers to the ServiceManagementService.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if an observer ID is invalid (not found in datastore)
|
|
*/
|
|
void ServiceManagementService::loadObservers()
|
|
{
|
|
util::loadObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this, m_dataStore);
|
|
}
|
|
|
|
/*
|
|
Function: saveObservers
|
|
Description: Saves the current observer IDs of the ServiceManagementService
|
|
to persistent storage for future retrieval.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::saveObservers()
|
|
{
|
|
util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this);
|
|
}
|
|
|
|
/*
|
|
Function: restoreInventory
|
|
Description: Restores inventory quantities for all required items in the services associated
|
|
with a given booking. Each item's quantity is incremented by a fixed value.
|
|
Parameter: ServiceBooking* booking - Pointer to the booking whose inventory items need to be restored
|
|
Return type: void
|
|
*/
|
|
static void restoreInventory(ServiceBooking* booking)
|
|
{
|
|
const int INCREMENT_VALUE = 1;
|
|
if (!booking)
|
|
{
|
|
return;
|
|
}
|
|
const auto& services = booking->getServices();
|
|
for (int serviceIterator = 0; serviceIterator < services.getSize(); ++serviceIterator)
|
|
{
|
|
Service* service = services.getValueAt(serviceIterator);
|
|
if (!service)
|
|
{
|
|
continue;
|
|
}
|
|
const auto& items = service->getRequiredInventoryItems();
|
|
for (int InventoryIterator = 0; InventoryIterator < items.getSize(); ++InventoryIterator)
|
|
{
|
|
InventoryItem* item = items.getValueAt(InventoryIterator);
|
|
if (item)
|
|
{
|
|
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: processBookingCancellation
|
|
Description: Cancels jobs and updates the status of a given booking. Sends notifications to the
|
|
specified user, resets technician assignment if needed, and restores inventory items.
|
|
Parameter: ServiceBooking* booking - Pointer to the booking being cancelled
|
|
util::ServiceJobStatus newServiceBookingStatus - New status to assign to the booking
|
|
const std::string& notificationTitle - Title of the booking cancellation notification
|
|
const std::string& notificationMessage - Message body of the booking cancellation notification
|
|
User* notifyUser - User to notify about the cancellation
|
|
util::ServiceJobStatus jobCardStatus - New status to assign to associated job cards
|
|
const std::string& jobNotificationTitle - Title of the job cancellation notification
|
|
const std::string& jobNotificationMessage - Message body of the job cancellation notification
|
|
util::Map<std::string, JobCard*>& jobs - Collection of job cards to update
|
|
ServiceManagementService& currentService - Reference to the service for sending notifications
|
|
Return type: void
|
|
*/
|
|
static void processBookingCancellation(ServiceBooking* booking,
|
|
util::ServiceJobStatus newServiceBookingStatus,
|
|
const std::string& notificationTitle,
|
|
const std::string& notificationMessage,
|
|
User* notifyUser,
|
|
util::ServiceJobStatus jobCardStatus,
|
|
const std::string& jobNotificationTitle,
|
|
const std::string& jobNotificationMessage,
|
|
util::Map<std::string, JobCard*>& jobs, ServiceManagementService& currentService)
|
|
{
|
|
if (!booking || !notifyUser)
|
|
{
|
|
return;
|
|
}
|
|
for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator)
|
|
{
|
|
JobCard* jobCard = jobs.getValueAt(jobIterator);
|
|
if (jobCard && jobCard->getBookingId() == booking->getId())
|
|
{
|
|
jobCard->setStatus(jobCardStatus);
|
|
currentService.sendNotification(notifyUser, jobNotificationTitle, jobNotificationMessage);
|
|
}
|
|
}
|
|
booking->setStatus(newServiceBookingStatus);
|
|
currentService.sendNotification(notifyUser, notificationTitle, notificationMessage);
|
|
if (newServiceBookingStatus == util::ServiceJobStatus::PENDING)
|
|
{
|
|
booking->setAssignedTechnician(nullptr);
|
|
booking->setAssignedTechnicianId("");
|
|
}
|
|
restoreInventory(booking);
|
|
}
|
|
|
|
/*
|
|
Function: cancelCustomerServiceBookings
|
|
Description: Cancels all service bookings associated with a given customer or technician.
|
|
Updates booking status, resets customer/technician assignments, sends notifications,
|
|
and restocks inventory items.
|
|
Parameter: const std::string& userID - ID of the customer or technician
|
|
Return type: void
|
|
*/
|
|
void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID)
|
|
{
|
|
auto& users = m_dataStore.getUsers();
|
|
int userIndex = users.find(customerID);
|
|
if (userIndex == -1)
|
|
{
|
|
throw std::runtime_error("User not found: " + customerID);
|
|
}
|
|
User* customer = users.getValueAt(userIndex);
|
|
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++)
|
|
{
|
|
ServiceBooking* booking = bookings.getValueAt(iteratorOne);
|
|
if (!booking)
|
|
{
|
|
continue;
|
|
}
|
|
if (booking->getCustomerId() != customerID)
|
|
{
|
|
continue;
|
|
}
|
|
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
|
|
{
|
|
continue;
|
|
}
|
|
User* assignedTechnician = booking->getAssignedTechnician();
|
|
std::string titleToTechnician = "Customer Service Cancelled";
|
|
std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked.";
|
|
std::string jobTitle = "Job Cancelled";
|
|
std::string jobMessage = "The job has been cancelled. Your job card has been cancelled and the inventory has been restocked.";
|
|
processBookingCancellation(booking,
|
|
util::ServiceJobStatus::CANCELLED,
|
|
titleToTechnician, messageToTechnician, assignedTechnician,
|
|
util::ServiceJobStatus::CANCELLED,
|
|
jobTitle, jobMessage, jobs, *this
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: cancelTechnicianJobs
|
|
Description: Cancels all jobs assigned to a technician. Updates job status, sends notifications,
|
|
and restocks inventory items used in the service.
|
|
Parameter: const std::string& technicianID - ID of the technician
|
|
Return type: void
|
|
*/
|
|
void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID)
|
|
{
|
|
auto& users = m_dataStore.getUsers();
|
|
int userIndex = users.find(technicianID);
|
|
if (userIndex == -1)
|
|
{
|
|
throw std::runtime_error("User not found: " + technicianID);
|
|
}
|
|
User* technician = users.getValueAt(userIndex);
|
|
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++)
|
|
{
|
|
ServiceBooking* booking = bookings.getValueAt(iteratorOne);
|
|
if (!booking)
|
|
{
|
|
continue;
|
|
}
|
|
std::string technicianId = booking->getAssignedTechnicianId();
|
|
if (technicianId != technicianID)
|
|
{
|
|
continue;
|
|
}
|
|
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
|
|
{
|
|
continue;
|
|
}
|
|
User* customer = booking->getCustomer();
|
|
if (!customer)
|
|
{
|
|
continue;
|
|
}
|
|
std::string title = "Technician Unavailable";
|
|
std::string message = "Your assigned technician is no longer available. Your booking has been reset to pending and we will reassign a new technician shortly.";
|
|
processBookingCancellation(booking,
|
|
util::ServiceJobStatus::PENDING,
|
|
title, message, customer,
|
|
util::ServiceJobStatus::CANCELLED,
|
|
title, message, jobs, *this
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: createComboPackage
|
|
Description: Creates a new combo package with two services and a discount percentage.
|
|
Validates service IDs, ensures uniqueness, and inserts the new package into the DataStore.
|
|
Parameter: const std::string& packageName - name of the combo package
|
|
const util::Vector<std::string>& serviceIDsInNewCombo - list of service IDs
|
|
double discountPercentage - discount percentage for the package
|
|
Return type: void
|
|
*/
|
|
void ServiceManagementService::createComboPackage(const std::string& packageName, const util::Vector<std::string>& serviceIDsInNewCombo, double discountPercentage)
|
|
{
|
|
if (packageName.empty())
|
|
{
|
|
throw std::invalid_argument("The Combo Package Name cannot be empty.\n");
|
|
}
|
|
if (serviceIDsInNewCombo.getSize() < 2 || serviceIDsInNewCombo.getSize() > 2)
|
|
{
|
|
throw std::invalid_argument("Combo package must contain only two services.");
|
|
}
|
|
if (discountPercentage < 0.0 || discountPercentage > 100.0)
|
|
{
|
|
throw std::invalid_argument("Discount percentage must be between 0 and 100.");
|
|
}
|
|
auto& servicesMap = m_dataStore.getServices();
|
|
for (int index = 0; index < serviceIDsInNewCombo.getSize(); index++)
|
|
{
|
|
const std::string serviceid = serviceIDsInNewCombo[index];
|
|
if (servicesMap.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++)
|
|
{
|
|
ComboPackage* existingCombos = comboPackageMap.getValueAt(iterator);
|
|
const util::Map<std::string, Service*>& servicesInsideExistingCombos = existingCombos->getServices();
|
|
if (servicesInsideExistingCombos.getSize() == serviceIDsInNewCombo.getSize())
|
|
{
|
|
bool isIdentical = true;
|
|
for (int serviceIterator = 0; serviceIterator < serviceIDsInNewCombo.getSize(); serviceIterator++)
|
|
{
|
|
const std::string& id = serviceIDsInNewCombo[serviceIterator];
|
|
if (servicesInsideExistingCombos.find(id) == -1)
|
|
{
|
|
isIdentical = false;
|
|
break;
|
|
}
|
|
}
|
|
if (isIdentical)
|
|
{
|
|
throw std::runtime_error("A combo package with the same services already exists.");
|
|
}
|
|
}
|
|
}
|
|
util::Map<std::string, Service*> selectedServices;
|
|
for (int iteratorOne = 0; iteratorOne < serviceIDsInNewCombo.getSize(); iteratorOne++)
|
|
{
|
|
const std::string& serviceId = serviceIDsInNewCombo[iteratorOne];
|
|
int serviceIndex = servicesMap.find(serviceId);
|
|
if (serviceIndex == -1)
|
|
{
|
|
throw std::runtime_error("Service ID not found: " + serviceId);
|
|
}
|
|
selectedServices.insert(serviceId, servicesMap.getValueAt(serviceIndex));
|
|
}
|
|
ComboPackage* newComboPackage = Factory::getObject<ComboPackage>(packageName, discountPercentage, selectedServices);
|
|
comboPackageMap.insert(newComboPackage->getId(), newComboPackage);
|
|
}
|
|
|
|
/*
|
|
Function: getComboPackages
|
|
Description: Retrieves all combo packages stored in the DataStore.
|
|
Parameter: None
|
|
Return type: util::Map<std::string, ComboPackage*>
|
|
*/
|
|
util::Map<std::string, ComboPackage*> ServiceManagementService::getComboPackages()
|
|
{
|
|
return m_dataStore.getComboPackages();
|
|
}
|
|
|
|
/*
|
|
Function: removeComboPackage
|
|
Description: Removes a combo package by marking it inactive. Throws an exception if the package ID is not found.
|
|
Parameter: const std::string& comboPackageID - ID of the combo package
|
|
Return type: void
|
|
*/
|
|
void ServiceManagementService::removeComboPackage(const std::string& comboPackageID)
|
|
{
|
|
bool removed = false;
|
|
util::Map<std::string, ComboPackage*>& currentComboPackages = m_dataStore.getComboPackages();
|
|
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
|
|
{
|
|
ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
|
|
if (currentComboPackage && currentComboPackage->getId() == comboPackageID)
|
|
{
|
|
currentComboPackage->setState(util::State::INACTIVE);
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!removed)
|
|
{
|
|
throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found.");
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: getServiceBookings
|
|
Description: Retrieves all service bookings from the datastore.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- util::Map<std::string, ServiceBooking*> containing all service bookings
|
|
*/
|
|
util::Map<std::string, ServiceBooking*> ServiceManagementService::getServiceBookings()
|
|
{
|
|
return m_dataStore.getServiceBookings();
|
|
}
|
|
|
|
/*
|
|
Function: getServiceBooking
|
|
Description: Retrieves a specific service booking by its ID.
|
|
Parameters:
|
|
- serviceID: std::string, ID of the service booking
|
|
Returns:
|
|
- ServiceBooking*: Pointer to the service booking, or nullptr if not found
|
|
*/
|
|
ServiceBooking* ServiceManagementService::getServiceBooking(const std::string& serviceID)
|
|
{
|
|
auto currentServiceBookings = getServiceBookings();
|
|
for (int iterator = 0; iterator < currentServiceBookings.getSize(); iterator++)
|
|
{
|
|
if (currentServiceBookings.getValueAt(iterator)->getId() == serviceID)
|
|
{
|
|
return currentServiceBookings.getValueAt(iterator);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/*
|
|
Function: createJobCard
|
|
Description: Creates a job card for a given service booking, service, and technician.
|
|
Validates booking, service, technician, and inventory availability before creation.
|
|
Parameters:
|
|
- bookingID: std::string, ID of the service booking
|
|
- technicianID: std::string, ID of the technician
|
|
- serviceID: std::string, ID of the service
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if booking, service, technician, or inventory validation fails
|
|
*/
|
|
void ServiceManagementService::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID)
|
|
{
|
|
UserManagementService m_userManagementService;
|
|
ServiceBooking* currentBooking = getServiceBooking(bookingID);
|
|
auto& currentJobCards = m_dataStore.getJobCards();
|
|
if (currentBooking == nullptr)
|
|
{
|
|
throw std::runtime_error("Service Booking not available");
|
|
}
|
|
auto& currentServices = currentBooking->getServices();
|
|
if (currentServices.find(serviceID) == -1)
|
|
{
|
|
throw std::runtime_error("Invalid service Id");
|
|
}
|
|
Service* currentService = currentServices.getValueAt(currentServices.find(serviceID));
|
|
User* selectedTechnician = m_userManagementService.getUser(technicianID);
|
|
if (selectedTechnician == nullptr)
|
|
{
|
|
throw std::runtime_error("Technician not available");
|
|
}
|
|
auto& inventoryItems = currentService->getRequiredInventoryItems();
|
|
for (int iterator = 0; iterator < inventoryItems.getSize(); iterator++)
|
|
{
|
|
InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iterator);
|
|
if (currentInventoryItem && currentInventoryItem->getQuantity() == 0)
|
|
{
|
|
std::string errorMessage = "Failed to create job card, " + currentInventoryItem->getPartName() + " is out of stock.";
|
|
throw std::runtime_error(errorMessage);
|
|
}
|
|
}
|
|
for (int iterator = 0; iterator < inventoryItems.getSize(); iterator++)
|
|
{
|
|
InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iterator);
|
|
if (currentInventoryItem)
|
|
{
|
|
int currentStockQuantity = currentInventoryItem->getQuantity();
|
|
currentInventoryItem->setQuantity(currentStockQuantity - 1);
|
|
}
|
|
}
|
|
currentBooking->setAssignedTechnician(selectedTechnician);
|
|
currentBooking->setAssignedTechnicianId(selectedTechnician->getId());
|
|
if (currentBooking->getStatus() == util::ServiceJobStatus::PENDING)
|
|
{
|
|
currentBooking->setStatus(util::ServiceJobStatus::STARTED);
|
|
}
|
|
std::string title = "Job card created";
|
|
std::string message = "Job card created for the service and you are assigned for that.";
|
|
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);
|
|
sendNotification(selectedTechnician, title, message);
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Failed to create job card.");
|
|
}
|
|
title = "Technician assigned";
|
|
message = "A technician has been assigned to your Service Booking with ID " + bookingID;
|
|
sendNotification(currentBooking->getCustomer(), title, message);
|
|
}
|
|
|
|
/*
|
|
Function: createService
|
|
Description: Creates a new service with associated inventory items and labor cost.
|
|
Validates inventory items before creation.
|
|
Parameters:
|
|
- name: std::string, name of the service
|
|
- inventoryItemIDs: util::Vector<std::string>, IDs of required inventory items
|
|
- laborCost: double, labor cost for the service
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if inventory items are not found or service creation fails
|
|
*/
|
|
void ServiceManagementService::createService(const std::string& name, const util::Vector<std::string>& inventoryItemIDs, double laborCost)
|
|
{
|
|
util::Map<std::string, InventoryItem*> currentServiceInventoryItems;
|
|
auto inventoryItems = m_dataStore.getInventoryItems();
|
|
for (int iteratorOne =0; iteratorOne < inventoryItemIDs.getSize(); iteratorOne++)
|
|
{
|
|
std::string currentItemID = inventoryItemIDs[iteratorOne];
|
|
bool itemFound = false;
|
|
for (int iteratorTwo = 0; iteratorTwo < inventoryItems.getSize(); iteratorTwo++)
|
|
{
|
|
InventoryItem* currentInventoryItem = inventoryItems.getValueAt(iteratorTwo);
|
|
if (currentInventoryItem && currentInventoryItem->getId() == currentItemID)
|
|
{
|
|
itemFound = true;
|
|
currentServiceInventoryItems.insert(currentInventoryItem->getId(), currentInventoryItem);
|
|
break;
|
|
}
|
|
}
|
|
if (!itemFound)
|
|
{
|
|
throw std::runtime_error("Inventory item with ID '" + currentItemID + "' not found.");
|
|
}
|
|
}
|
|
Service* newService = Factory::getObject<Service>(name, currentServiceInventoryItems, laborCost);
|
|
if (newService == nullptr)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
Function: getServices
|
|
Description: Retrieves all services from the datastore.
|
|
Parameters:
|
|
- None
|
|
Returns:
|
|
- util::Map<std::string, Service*> containing all services
|
|
*/
|
|
util::Map<std::string, Service*> ServiceManagementService::getServices()
|
|
{
|
|
return m_dataStore.getServices();
|
|
}
|
|
|
|
/*
|
|
Function: removeService
|
|
Description: Marks a service as inactive by its ID.
|
|
Parameters:
|
|
- serviceID: std::string, ID of the service
|
|
Returns:
|
|
- void
|
|
Throws:
|
|
- std::runtime_error if the service is not found
|
|
*/
|
|
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)
|
|
{
|
|
currentServices.getValueAt(currentServices.find(serviceID))->setState(util::State::INACTIVE);
|
|
for (int iterator = 0; iterator < currentComboPackages.getSize(); iterator++)
|
|
{
|
|
ComboPackage* currentComboPackage = currentComboPackages.getValueAt(iterator);
|
|
if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE)
|
|
{
|
|
util::Map<std::string, Service*> currentServices = currentComboPackage->getServices();
|
|
for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
|
|
{
|
|
auto currentService = currentServices.getValueAt(iterator);
|
|
if (currentService->getId() == serviceID)
|
|
{
|
|
currentComboPackage->setState(util::State::INACTIVE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Service not found.");
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: getServiceBookings (overloaded)
|
|
Description: Retrieves all service bookings for a specific customer.
|
|
Parameters:
|
|
- customerID: std::string, ID of the customer
|
|
Returns:
|
|
- util::Map<std::string, ServiceBooking*> containing bookings for the customer
|
|
*/
|
|
util::Map<std::string, ServiceBooking*> ServiceManagementService::getServiceBookings(const std::string& customerID)
|
|
{
|
|
util::Map<std::string, ServiceBooking*> currentServiceBookings = getServiceBookings();
|
|
util::Map<std::string, ServiceBooking*> currentUserServiceBookings;
|
|
if (currentServiceBookings.getSize() != 0)
|
|
{
|
|
for (int iterator = 0; iterator < currentServiceBookings.getSize(); iterator++)
|
|
{
|
|
auto currentServiceBooking = currentServiceBookings.getValueAt(iterator);
|
|
if (currentServiceBooking->getCustomerId() == customerID)
|
|
{
|
|
currentUserServiceBookings.insert(currentServiceBooking->getId(), currentServiceBooking);
|
|
}
|
|
}
|
|
}
|
|
return currentUserServiceBookings;
|
|
}
|
|
|
|
/*
|
|
Function: getJobCards
|
|
Description: Retrieves all job cards assigned to a specific technician.
|
|
Parameters:
|
|
- technicianID: std::string, ID of the technician
|
|
Returns:
|
|
- util::Map<std::string, JobCard*> containing job cards assigned to the technician
|
|
*/
|
|
util::Map<std::string, JobCard*> ServiceManagementService::getJobCards(const std::string& technicianID)
|
|
{
|
|
util::Map<std::string, JobCard*> jobCards = m_dataStore.getJobCards();
|
|
util::Map<std::string, JobCard*> technicianJobCards;
|
|
for (int iterator = 0; iterator < jobCards.getSize(); iterator++)
|
|
{
|
|
JobCard* currentJobCard = jobCards.getValueAt(iterator);
|
|
if (currentJobCard->getTechnicianId() == technicianID)
|
|
{
|
|
technicianJobCards.insert(currentJobCard->getId(), currentJobCard);
|
|
}
|
|
}
|
|
return technicianJobCards;
|
|
}
|
|
|
|
/*
|
|
Function: hasCompletedAllJobs (static helper)
|
|
Description: Checks if all job cards for a given service booking are completed.
|
|
Parameters:
|
|
- bookingId: std::string, ID of the service booking
|
|
- currentAssignedJobs: util::Map<std::string, JobCard*>&, map of assigned job cards
|
|
Returns:
|
|
- bool: True if all job cards are completed, False otherwise
|
|
*/
|
|
static bool hasCompletedAllJobs(std::string bookingId, util::Map<std::string, JobCard*>& currentAssignedJobs)
|
|
{
|
|
for (int iterator = 0; iterator < currentAssignedJobs.getSize(); iterator++)
|
|
{
|
|
JobCard* currentJob = currentAssignedJobs.getValueAt(iterator);
|
|
if (currentJob->getBookingId() == bookingId)
|
|
{
|
|
if (currentJob->getStatus() != util::ServiceJobStatus::COMPLETED && currentJob->getStatus() != util::ServiceJobStatus::CANCELLED)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Function: updateJobStatus
|
|
Description:
|
|
Updates the status of a job card assigned to the currently authenticated technician.
|
|
- If the job is STARTED, it moves to IN_PROGRESS.
|
|
- If the job is IN_PROGRESS, it moves to COMPLETED.
|
|
When all jobs in a service booking are completed, the booking status is updated,
|
|
an invoice is generated, and a notification is sent to the customer.
|
|
Parameters:
|
|
- jobID: const std::string&, unique identifier of the job card to update.
|
|
Returns:
|
|
- void
|
|
*/
|
|
void ServiceManagementService::updateJobStatus(const std::string& jobID)
|
|
{
|
|
AuthenticationManagementService authenticationManagementService;
|
|
PaymentManagementService paymentManagementService;
|
|
bool jobStatusUpdated = false, serviceBookingCompleted;
|
|
JobCard* currentJob;
|
|
User* currentTechnician = authenticationManagementService.getAuthenticatedUser();
|
|
if (currentTechnician == nullptr)
|
|
{
|
|
throw std::runtime_error("Unable to fetch current technician.");
|
|
}
|
|
util::Map<std::string, JobCard*> currentAssignedJobs = getJobCards(currentTechnician->getId());
|
|
if (currentAssignedJobs.getSize() == 0)
|
|
{
|
|
throw std::runtime_error("No job cards assigned to the technician.");
|
|
}
|
|
if (currentAssignedJobs.find(jobID) != -1)
|
|
{
|
|
currentJob = currentAssignedJobs.getValueAt(currentAssignedJobs.find(jobID));
|
|
if (currentJob == nullptr)
|
|
{
|
|
throw std::runtime_error("Unable to fetch current job.");
|
|
}
|
|
if (currentJob->getStatus() == util::ServiceJobStatus::STARTED)
|
|
{
|
|
currentJob->setStatus(util::ServiceJobStatus::IN_PROGRESS);
|
|
jobStatusUpdated = true;
|
|
}
|
|
else if (currentJob->getStatus() == util::ServiceJobStatus::IN_PROGRESS)
|
|
{
|
|
currentJob->setStatus(util::ServiceJobStatus::COMPLETED);
|
|
jobStatusUpdated = true;
|
|
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
|
|
if (serviceBookingCompleted)
|
|
{
|
|
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
|
|
paymentManagementService.generateInvoice(currentJob->getBooking());
|
|
std::string title = "Service Booking completed. Invoice Generated.";
|
|
std::string message = "Services completed for the booking and invoice generated.";
|
|
sendNotification(currentJob->getBooking()->getCustomer(), title, message);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Failed to update job status. Job may already be completed.");
|
|
}
|
|
if (!jobStatusUpdated)
|
|
{
|
|
throw std::runtime_error("Failed to update job status. Job may already be completed.");
|
|
}
|
|
} |