966 lines
38 KiB
C++
966 lines
38 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 "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 "DataStoreLockGuard.h"
|
|
#include "Utility.h"
|
|
#include "DataStoreLockGuard.h"
|
|
#include "EventManager.h"
|
|
|
|
/*
|
|
Function: notifyAllAdmins
|
|
Description: Sends a notification to all users with the ADMIN role.
|
|
Iterates through the provided user collection and delivers
|
|
the specified notification to each administrator using the
|
|
ServiceManagementService notification mechanism.
|
|
Parameter: const std::string& title - notification title
|
|
Parameter: const std::string& message - notification message
|
|
Parameter: const util::Map<std::string, TrackedRecord<User>>& users - collection of tracked users
|
|
Parameter: ServiceManagementService* serviceManagementService - service used to dispatch notifications
|
|
Return type: void
|
|
*/
|
|
static void notifyAllAdmins(const std::string& title, const std::string& message, const util::Map<std::string, TrackedRecord<User>>& users, ServiceManagementService* serviceManagementService)
|
|
{
|
|
int numberOfUsers = users.getSize();
|
|
for (int index = 0; index < numberOfUsers; index++)
|
|
{
|
|
User* user = users.getValueAt(index).data;
|
|
if (user->getUserType() == util::UserType::ADMIN)
|
|
{
|
|
serviceManagementService->sendNotification(user, title, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
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::ensureAuthorization();
|
|
AuthenticationManagementService m_authenticationManagementService;
|
|
auto authenticatedUser = m_authenticationManagementService.getAuthenticatedUser();
|
|
if (authenticatedUser == nullptr)
|
|
{
|
|
throw std::runtime_error("No user is currently logged in!");
|
|
}
|
|
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 = trackedServicesMap.find(serviceIDs[index]);
|
|
if (serviceIndex == -1)
|
|
{
|
|
throw std::runtime_error("Service not found!");
|
|
}
|
|
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);
|
|
if (serviceBooking == nullptr)
|
|
{
|
|
throw std::runtime_error("Failed to create service booking");
|
|
}
|
|
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);
|
|
m_dataStore.saveServiceBookings();
|
|
notifyAllAdmins("New Service Order Available", "A new service order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this);
|
|
}
|
|
|
|
/*
|
|
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::ensureAuthorization();
|
|
AuthenticationManagementService m_authenticationManagementService;
|
|
auto authenticatedUser = m_authenticationManagementService.getAuthenticatedUser();
|
|
if (authenticatedUser == nullptr)
|
|
{
|
|
throw std::runtime_error("No user is currently logged in!");
|
|
}
|
|
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 = 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");
|
|
}
|
|
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);
|
|
notifyAllAdmins("New Combo Package Order Available", "A new combo package order has been placed with Service Booking ID " + serviceBooking->getId(), m_dataStore.getUsers(), this);
|
|
}
|
|
|
|
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)
|
|
{
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
m_observers.clear();
|
|
m_observers = m_dataStore.getServiceManagementObservers();
|
|
if (user)
|
|
{
|
|
const std::string& userID = user->getId();
|
|
if (m_observers.find(userID) == -1)
|
|
{
|
|
m_observers[userID] = user;
|
|
}
|
|
}
|
|
m_dataStore.saveServiceManagementObservers(m_observers);
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
m_observers.clear();
|
|
m_observers = m_dataStore.getServiceManagementObservers();
|
|
if (user)
|
|
{
|
|
const std::string& userID = user->getId();
|
|
if (m_observers.find(userID) != -1)
|
|
{
|
|
m_observers.remove(userID);
|
|
}
|
|
}
|
|
m_dataStore.saveServiceManagementObservers(m_observers);
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
return;
|
|
}
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
m_observers = m_dataStore.getServiceManagementObservers();
|
|
if (m_observers.find(user->getId()) == -1)
|
|
{
|
|
return;
|
|
}
|
|
auto& trackedNotificationsMap = m_dataStore.getNotifications();
|
|
Notification* notification = Factory::getObject<Notification>(
|
|
user->getId(),
|
|
title,
|
|
message,
|
|
util::Timestamp());
|
|
if (!notification)
|
|
{
|
|
throw std::runtime_error("Failed to create notification");
|
|
}
|
|
trackedNotificationsMap.insert(notification->getId(), util::createNewRecord(notification));
|
|
m_dataStore.saveNotifications();
|
|
EventManager::sendNotificationAvailableEvent(user->getId());
|
|
}
|
|
|
|
/*
|
|
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, util::Map<std::string, TrackedRecord<InventoryItem>>& trackedInventoryItems)
|
|
{
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: processBookingCancellation
|
|
Description: Handles cancellation or reassignment of a service booking based on user type.
|
|
Cancels associated job cards, updates booking status, clears technician assignments,
|
|
restores inventory, and sends appropriate notifications.
|
|
Parameters:
|
|
ServiceBooking* booking - The booking to cancel or reset
|
|
util::Map<std::string, JobCard*>& jobs - Collection of job cards to update
|
|
ServiceManagementService& currentService - Service layer for notifications
|
|
util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN)
|
|
Return type: void
|
|
*/
|
|
static void processBookingCancellation(TrackedRecord<ServiceBooking>& trackedBooking,
|
|
util::Map<std::string, TrackedRecord<JobCard>>& jobs,
|
|
ServiceManagementService& currentService,
|
|
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)
|
|
{
|
|
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())
|
|
{
|
|
const std::string jobTitle = "Job Cancelled";
|
|
const std::string jobMessage = "Your job card has been cancelled and the inventory has been restocked.";
|
|
currentService.sendNotification(technician, jobTitle, jobMessage);
|
|
}
|
|
}
|
|
}
|
|
if (userType == util::UserType::CUSTOMER)
|
|
{
|
|
booking->setStatus(util::ServiceJobStatus::CANCELLED);
|
|
if (User* technician = booking->getAssignedTechnician())
|
|
{
|
|
const std::string title = "Customer Service Cancelled";
|
|
const std::string message = "Your assigned job card has been cancelled and the inventory has been restocked.";
|
|
currentService.sendNotification(technician, title, message);
|
|
}
|
|
}
|
|
else if (userType == util::UserType::TECHNICIAN)
|
|
{
|
|
booking->setStatus(util::ServiceJobStatus::PENDING);
|
|
if (User* customer = booking->getCustomer())
|
|
{
|
|
const std::string title = "Technician Unavailable";
|
|
const std::string message = "Your booking has been reset to pending and we will reassign a new technician shortly.";
|
|
currentService.sendNotification(customer, title, message);
|
|
}
|
|
}
|
|
booking->setAssignedTechnician(nullptr);
|
|
booking->setAssignedTechnicianId("");
|
|
trackedBooking.state = RecordState::MODIFIED;
|
|
restoreInventory(booking, trackedInventoryItems);
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
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 = trackedUsers.getValueAt(userIndex).data;
|
|
if (!customer)
|
|
{
|
|
throw std::runtime_error("User not found: " + customerID);
|
|
}
|
|
auto& trackedBookings = m_dataStore.getServiceBookings();
|
|
auto& trackedJobs = m_dataStore.getJobCards();
|
|
auto& trackedInventoryItems = m_dataStore.getInventoryItems();
|
|
for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++)
|
|
{
|
|
auto& trackedBooking = trackedBookings.getValueAt(iteratorOne);
|
|
ServiceBooking* booking = trackedBooking.data;
|
|
if (!booking)
|
|
{
|
|
continue;
|
|
}
|
|
if (booking->getCustomerId() != customerID)
|
|
{
|
|
continue;
|
|
}
|
|
if (booking->getStatus() != util::ServiceJobStatus::PENDING &&
|
|
booking->getStatus() != util::ServiceJobStatus::STARTED &&
|
|
booking->getStatus() != util::ServiceJobStatus::IN_PROGRESS)
|
|
{
|
|
continue;
|
|
}
|
|
processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::CUSTOMER, trackedInventoryItems);
|
|
}
|
|
m_dataStore.saveUsers();
|
|
m_dataStore.saveServiceBookings();
|
|
m_dataStore.saveJobCards();
|
|
m_dataStore.saveInventoryItems();
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
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 = trackedUsers.getValueAt(userIndex).data;
|
|
if (!technician)
|
|
{
|
|
throw std::runtime_error("User not found: " + technicianID);
|
|
}
|
|
auto& trackedBookings = m_dataStore.getServiceBookings();
|
|
auto& trackedJobs = m_dataStore.getJobCards();
|
|
auto& trackedInventoryItems = m_dataStore.getInventoryItems();
|
|
for (int iteratorOne = 0; iteratorOne < trackedBookings.getSize(); iteratorOne++)
|
|
{
|
|
auto& trackedBooking = trackedBookings.getValueAt(iteratorOne);
|
|
ServiceBooking* booking = trackedBooking.data;
|
|
if (!booking)
|
|
{
|
|
continue;
|
|
}
|
|
std::string technicianId = booking->getAssignedTechnicianId();
|
|
if (technicianId != technicianID)
|
|
{
|
|
continue;
|
|
}
|
|
if (booking->getStatus() != util::ServiceJobStatus::PENDING &&
|
|
booking->getStatus() != util::ServiceJobStatus::STARTED &&
|
|
booking->getStatus() != util::ServiceJobStatus::IN_PROGRESS)
|
|
{
|
|
continue;
|
|
}
|
|
User* customer = booking->getCustomer();
|
|
if (!customer)
|
|
{
|
|
continue;
|
|
}
|
|
processBookingCancellation(trackedBooking, trackedJobs, *this, util::UserType::TECHNICIAN, trackedInventoryItems);
|
|
}
|
|
m_dataStore.saveUsers();
|
|
m_dataStore.saveInventoryItems();
|
|
m_dataStore.saveServiceBookings();
|
|
m_dataStore.saveJobCards();
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
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& trackedServicesMap = m_dataStore.getServices();
|
|
for (int index = 0; index < serviceIDsInNewCombo.getSize(); index++)
|
|
{
|
|
const std::string& serviceid = serviceIDsInNewCombo[index];
|
|
if (trackedServicesMap.find(serviceid) == -1)
|
|
{
|
|
throw std::runtime_error("Service ID not found: " + serviceid);
|
|
}
|
|
}
|
|
auto& trackedComboPackageMap = m_dataStore.getComboPackages();
|
|
for (int iterator = 0; iterator < trackedComboPackageMap.getSize(); iterator++)
|
|
{
|
|
ComboPackage* existingCombos = trackedComboPackageMap.getValueAt(iterator).data;
|
|
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 = trackedServicesMap.find(serviceId);
|
|
if (serviceIndex == -1)
|
|
{
|
|
throw std::runtime_error("Service ID not found: " + serviceId);
|
|
}
|
|
selectedServices.insert(serviceId, trackedServicesMap.getValueAt(serviceIndex).data);
|
|
}
|
|
ComboPackage* newComboPackage = Factory::getObject<ComboPackage>(packageName, discountPercentage, selectedServices);
|
|
trackedComboPackageMap.insert(newComboPackage->getId(), util::createNewRecord(newComboPackage));
|
|
m_dataStore.saveComboPackages();
|
|
}
|
|
|
|
/*
|
|
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()
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
util::Map<std::string, ComboPackage*> comboPackages;
|
|
comboPackages = util::getObjects(m_dataStore.getComboPackages());
|
|
return comboPackages;
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
bool removed = false;
|
|
auto& trackedComboPackages = m_dataStore.getComboPackages();
|
|
for (int iterator = 0; iterator < trackedComboPackages.getSize(); 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;
|
|
}
|
|
}
|
|
if (!removed)
|
|
{
|
|
throw std::runtime_error("Combo package with ID '" + comboPackageID + "' not found.");
|
|
}
|
|
m_dataStore.saveComboPackages();
|
|
}
|
|
|
|
/*
|
|
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()
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
util::Map<std::string, ServiceBooking*> serviceBookings;
|
|
serviceBookings = util::getObjects(m_dataStore.getServiceBookings());
|
|
return serviceBookings;
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
UserManagementService m_userManagementService;
|
|
ServiceBooking* currentBooking = getServiceBooking(bookingID);
|
|
auto& currentTrackedJobCards = 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)
|
|
{
|
|
currentTrackedJobCards.insert(jobCard->getId(), util::createNewRecord(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);
|
|
m_dataStore.saveJobCards();
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
util::Map<std::string, InventoryItem*> currentServiceInventoryItems;
|
|
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 < trackedInventoryItems.getSize(); iteratorTwo++)
|
|
{
|
|
InventoryItem* currentInventoryItem = trackedInventoryItems.getValueAt(iteratorTwo).data;
|
|
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.");
|
|
}
|
|
if (currentServices.find(newService->getId()) != -1)
|
|
{
|
|
throw std::runtime_error("Service with this ID Already exists.");
|
|
}
|
|
currentServices.insert(newService->getId(), util::createNewRecord(newService));
|
|
m_dataStore.saveServices();
|
|
}
|
|
|
|
/*
|
|
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()
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
util::Map<std::string, Service*> services;
|
|
services = util::getObjects(m_dataStore.getServices());
|
|
return services;
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
auto& currentTrackedServices = m_dataStore.getServices();
|
|
auto& currentTrackedComboPackages = m_dataStore.getComboPackages();
|
|
if (currentTrackedServices.find(serviceID) != -1)
|
|
{
|
|
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++)
|
|
{
|
|
comboPackageIndex = iterator;
|
|
ComboPackage* currentComboPackage = currentTrackedComboPackages.getValueAt(iterator).data;
|
|
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);
|
|
currentTrackedComboPackages.getValueAt(comboPackageIndex).state = RecordState::MODIFIED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Service not found.");
|
|
}
|
|
m_dataStore.saveServices();
|
|
m_dataStore.saveComboPackages();
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
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)
|
|
{
|
|
AuthenticationManagementService::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
auto& trackedJobCards = m_dataStore.getJobCards();
|
|
util::Map<std::string, JobCard*> technicianJobCards;
|
|
for (int iterator = 0; iterator < trackedJobCards.getSize(); iterator++)
|
|
{
|
|
JobCard* currentJobCard = trackedJobCards.getValueAt(iterator).data;
|
|
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::ensureAuthorization();
|
|
DataStoreLockGuard lock(m_dataStore);
|
|
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.");
|
|
}
|
|
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)
|
|
{
|
|
throw std::runtime_error("Unable to fetch current job.");
|
|
}
|
|
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.";
|
|
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.");
|
|
}
|
|
m_dataStore.saveJobCards();
|
|
m_dataStore.saveServiceBookings();
|
|
} |