Implement Purchase Individual Service functionality

<UserStory> CUS006 Purchase Individual Service </UserStory>

<Changes>
    1. Added service selection flow in CustomerMenu to display active services, allow customer to choose a service, and collect vehicle details for booking.
    2. Implemented Controller::purchaseService to delegate service booking requests to ServiceManagementService.
    3. Added ServiceManagementService::purchaseService logic to validate authenticated user, fetch selected services, create a ServiceBooking, and persist it in DataStore.
    4. Updated ServiceBooking constructor and assigned technician handling to use User* references instead of technician name strings.
    5. Integrated ServiceManagementService dependency into Controller
</Changes>

<Test>

 Validate customer service booking flow and service status tracking

  Precondition:
  1. Customer account exists and is logged into the system.
  2. Active services are available in the system.
  3. Service bookings can be created and stored in DataStore.
  4. Technician account exists to update service status.

  Steps:
  1. Navigate to Customer Menu and select the individual service booking option.
  2. View the list of active services and select a service.
  3. Enter vehicle number, vehicle brand, and vehicle model, then confirm booking.
    - Verify that the service booking is created successfully with an initial status.
  4. Technician updates the service booking status.
    - Verify that the latest status is reflected in the system.
  5. Customer refreshes or views service booking history.
    - Verify that the updated status is shown correctly in booking history.

</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
This commit is contained in:
2026-05-19 22:39:32 +05:30
parent 56c5c2dc70
commit 76f13b526e
9 changed files with 183 additions and 13 deletions
@@ -176,6 +176,7 @@
<ClInclude Include="utilities\Map.h" /> <ClInclude Include="utilities\Map.h" />
<ClInclude Include="utilities\OutputHelper.h" /> <ClInclude Include="utilities\OutputHelper.h" />
<ClInclude Include="utilities\Timestamp.h" /> <ClInclude Include="utilities\Timestamp.h" />
<ClInclude Include="utilities\Utility.h" />
<ClInclude Include="utilities\Validator.h" /> <ClInclude Include="utilities\Validator.h" />
<ClInclude Include="utilities\Vector.h" /> <ClInclude Include="utilities\Vector.h" />
<ClInclude Include="views\AdminMenu.h" /> <ClInclude Include="views\AdminMenu.h" />
@@ -42,6 +42,7 @@ util::Map<std::string, const ComboPackage*> Controller::getComboPackages()
void Controller::purchaseService(const util::Vector<std::string>& serviceIDs, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel) void Controller::purchaseService(const util::Vector<std::string>& serviceIDs, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel)
{ {
m_serviceManagementService.purchaseService(serviceIDs, vehicleNumber, vehicleBrand, vehicleModel);
} }
void Controller::purchaseComboPackage(const std::string& comboPackageID, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel) void Controller::purchaseComboPackage(const std::string& comboPackageID, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel)
@@ -2,6 +2,7 @@
#include "Map.h" #include "Map.h"
#include <string> #include <string>
#include "Enums.h" #include "Enums.h"
#include "ServiceManagementService.h"
class Service; class Service;
class ComboPackage; class ComboPackage;
@@ -14,6 +15,8 @@ class Notification;
class Controller class Controller
{ {
private:
ServiceManagementService m_serviceManagementService;
public: public:
bool login(const std::string& username, const std::string& password); bool login(const std::string& username, const std::string& password);
void logout(); void logout();
@@ -5,10 +5,10 @@ int ServiceBooking::m_uid = 0;
ServiceBooking::ServiceBooking() ServiceBooking::ServiceBooking()
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SRV" + std::to_string(++m_uid)),
m_customer(nullptr), m_customer(nullptr),
m_assignedTechnician(nullptr),
m_discountPercentage(0.0) {} m_discountPercentage(0.0) {}
ServiceBooking::ServiceBooking( ServiceBooking::ServiceBooking(
const std::string& id,
util::ServiceJobStatus status, util::ServiceJobStatus status,
const util::Map<std::string, const util::Map<std::string,
Service*>& services, Service*>& services,
@@ -17,8 +17,6 @@ ServiceBooking::ServiceBooking(
const std::string& vehicleNumber, const std::string& vehicleNumber,
const std::string& vehicleBrand, const std::string& vehicleBrand,
const std::string& vehicleModel, const std::string& vehicleModel,
const std::string& assignedTechnicianId,
const std::string& assignedTechnician,
double discountPercentage double discountPercentage
) )
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SRV" + std::to_string(++m_uid)),
@@ -29,8 +27,8 @@ ServiceBooking::ServiceBooking(
m_vehicleNumber(vehicleNumber), m_vehicleNumber(vehicleNumber),
m_vehicleBrand(vehicleBrand), m_vehicleBrand(vehicleBrand),
m_vehicleModel(vehicleModel), m_vehicleModel(vehicleModel),
m_assignedTechnicianId(assignedTechnicianId), m_assignedTechnicianId(""),
m_assignedTechnician(assignedTechnician), m_assignedTechnician(nullptr),
m_discountPercentage(discountPercentage) m_discountPercentage(discountPercentage)
{ {
} }
@@ -80,7 +78,7 @@ const std::string& ServiceBooking::getAssignedTechnicianId() const
return m_assignedTechnicianId; return m_assignedTechnicianId;
} }
const std::string& ServiceBooking::getAssignedTechnician() const User* ServiceBooking::getAssignedTechnician() const
{ {
return m_assignedTechnician; return m_assignedTechnician;
} }
@@ -135,7 +133,7 @@ void ServiceBooking::setAssignedTechnicianId(const std::string& assignedTechnici
m_assignedTechnicianId = assignedTechnicianId; m_assignedTechnicianId = assignedTechnicianId;
} }
void ServiceBooking::setAssignedTechnician(const std::string& assignedTechnician) void ServiceBooking::setAssignedTechnician(User* assignedTechnician)
{ {
m_assignedTechnician = assignedTechnician; m_assignedTechnician = assignedTechnician;
} }
@@ -19,12 +19,11 @@ private:
std::string m_vehicleBrand; std::string m_vehicleBrand;
std::string m_vehicleModel; std::string m_vehicleModel;
std::string m_assignedTechnicianId; std::string m_assignedTechnicianId;
std::string m_assignedTechnician; User* m_assignedTechnician;
double m_discountPercentage; double m_discountPercentage;
public: public:
ServiceBooking(); ServiceBooking();
ServiceBooking( ServiceBooking(
const std::string& id,
util::ServiceJobStatus status, util::ServiceJobStatus status,
const util::Map<std::string, const util::Map<std::string,
Service*>& services, Service*>& services,
@@ -33,8 +32,6 @@ public:
const std::string& vehicleNumber, const std::string& vehicleNumber,
const std::string& vehicleBrand, const std::string& vehicleBrand,
const std::string& vehicleModel, const std::string& vehicleModel,
const std::string& assignedTechnicianId,
const std::string& assignedTechnician,
double discountPercentage double discountPercentage
); );
const std::string& getId() const; const std::string& getId() const;
@@ -46,7 +43,7 @@ public:
const std::string& getVehicleBrand() const; const std::string& getVehicleBrand() const;
const std::string& getVehicleModel() const; const std::string& getVehicleModel() const;
const std::string& getAssignedTechnicianId() const; const std::string& getAssignedTechnicianId() const;
const std::string& getAssignedTechnician() const; User* getAssignedTechnician() const;
double getDiscountPercentage() const; double getDiscountPercentage() const;
void setId(const std::string& id); void setId(const std::string& id);
void setStatus(const util::ServiceJobStatus& status); void setStatus(const util::ServiceJobStatus& status);
@@ -57,6 +54,6 @@ public:
void setVehicleBrand(const std::string& vehicleBrand); void setVehicleBrand(const std::string& vehicleBrand);
void setVehicleModel(const std::string& vehicleModel); void setVehicleModel(const std::string& vehicleModel);
void setAssignedTechnicianId(const std::string& assignedTechnicianId); void setAssignedTechnicianId(const std::string& assignedTechnicianId);
void setAssignedTechnician(const std::string& assignedTechnician); void setAssignedTechnician(User* assignedTechnician);
void setDiscountPercentage(double discountPercentage); void setDiscountPercentage(double discountPercentage);
}; };
@@ -1 +1,39 @@
#include <stdexcept>
#include "ServiceManagementService.h" #include "ServiceManagementService.h"
#include "AuthenticationManagementService.h"
#include "Service.h"
#include "ServiceBooking.h"
#include "Factory.h"
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::STARTED, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, 0);
if (serviceBooking == nullptr)
{
throw std::runtime_error("Failed to create service booking");
}
serviceBookingMap[serviceBooking->getId()] = serviceBooking;
sendNotification(authenticatedUser,
"Service Booking succeeded",
"Your service booking has been successfully placed with ID " + serviceBooking->getId());
}
@@ -0,0 +1,15 @@
#pragma once
#include "Service.h"
#include "InventoryItem.h"
inline double calculatePartsCost(const Service* service)
{
double cost = 0;
auto& requiredInventoryItems = service->getRequiredInventoryItems();
int requiredInventoryItemsSize = requiredInventoryItems.getSize();
for (int index = 0; index < requiredInventoryItemsSize; index++)
{
cost += requiredInventoryItems.getValueAt(index)->getPrice();
}
return cost;
}
@@ -1,6 +1,12 @@
#include <iomanip>
#include "CustomerMenu.h" #include "CustomerMenu.h"
#include "Service.h"
#include "InventoryItem.h"
#include "InputHelper.h" #include "InputHelper.h"
#include "OutputHelper.h" #include "OutputHelper.h"
#include "Vector.h"
#include "Utility.h"
#include "Map.h"
void CustomerMenu::showMenu() void CustomerMenu::showMenu()
{ {
@@ -43,8 +49,73 @@ void CustomerMenu::updateDetails()
{ {
} }
static const Service* selectServiceFromServices(const util::Map<std::string, const Service*>& services)
{
util::Map<int, const Service*> activeServicesMap;
int currentIndex = 1;
int userInputIndex;
std::cout << std::left
<< std::setw(10) << "Index"
<< std::setw(15) << "Service ID"
<< std::setw(25) << "Service Name"
<< std::setw(15) << "Estimated Cost"
<< std::endl;
for (int index = 0; index < services.getSize(); index++)
{
const Service* currentService = services.getValueAt(index);
if (currentService->getState() != util::State::ACTIVE)
{
continue;
}
activeServicesMap.insert(currentIndex, currentService);
double partsCost = calculatePartsCost(currentService);
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(15) << currentService->getId()
<< std::setw(25) << currentService->getName()
<< std::setw(15) << (currentService->getLaborCost() + partsCost)
<< std::endl;
currentIndex++;
}
if (activeServicesMap.getSize() == 0)
{
std::cout << "No active services available." << std::endl;
return nullptr;
}
std::cout << "Enter service index: ";
util::read(userInputIndex);
if (activeServicesMap.find(userInputIndex) == -1)
{
std::cout << "Invalid service index." << std::endl;
return nullptr;
}
return activeServicesMap[userInputIndex];
}
void CustomerMenu::selectService() void CustomerMenu::selectService()
{ {
std::string vehicleNumber, vehicleBrand, vehicleModel;
auto services = m_controller.getServices();
util::Vector<std::string> selectedServices;
util::clear();
const Service* selectedService = selectServiceFromServices(services);
if (selectedService == nullptr)
{
std::cout << "Failed to book service!";
util::pressEnter();
return;
}
selectedServices.push_back(selectedService->getId());
util::clear();
std::cout << "Enter vehicle number: ";
util::read(vehicleNumber);
std::cout << "Enter vehicle brand: ";
util::read(vehicleBrand);
std::cout << "Enter vehicle model: ";
util::read(vehicleModel);
m_controller.purchaseService(selectedServices, vehicleNumber, vehicleBrand, vehicleModel);
std::cout << "Service has been booked successfully";
util::pressEnter();
} }
void CustomerMenu::selectComboPackage() void CustomerMenu::selectComboPackage()
@@ -0,0 +1,46 @@
#pragma once
#include <string>
#include <iomanip>
#include "Notification.h"
#include "Map.h"
#include "InputHelper.h"
#include "OutputHelper.h"
inline const Notification* selectNotification(const util::Vector<const Notification*>& notifications)
{
if (notifications.getSize() == 0)
{
std::cout << "No notifications available." << std::endl;
return nullptr;
}
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(15) << "ID"
<< std::setw(30) << "Title"
<< std::setw(25) << "Timestamp"
<< std::endl;
int currentIndex = 1;
for (int iterator = 0; iterator < notifications.getSize(); iterator++)
{
const Notification* currentNotification = notifications[iterator];
if (currentNotification)
{
std::cout << std::left
<< std::setw(6) << currentIndex
<< std::setw(15) << currentNotification->getId()
<< std::setw(30) << currentNotification->getTitle()
<< std::setw(25) << currentNotification->getCreatedAt().toString()
<< std::endl;
currentIndex++;
}
}
int selectedIndex;
std::cout << "Select notification: ";
util::read(selectedIndex);
if (selectedIndex < 1 || selectedIndex > notifications.getSize())
{
std::cout << "Invalid selection." << std::endl;
return nullptr;
}
return notifications[selectedIndex - 1];
}