Implement Update Service Status

<UserStory> SER005: Update Service Status </UserStory>

<Changes>
    1. Integrated Controller with ServiceManagementService to support job completion workflow.
    2. Implemented ServiceManagementService::completeJob with validation for technician assignment, job existence, and status transition.
    3. Enhanced TechnicianMenu with job selection helper to display assigned jobs, allow index-based selection, and handle invalid choices.
    4. Updated TechnicianMenu::completeJob to mark job as completed via Controller and provide user feedback.
    5. Added logic to check if all jobs in a booking are completed, triggering invoice generation and customer notification.
</Changes>

<Test>

 Acceptance Criteria:
 1. Status updates are visible to customers immediately after technician marks job as completed.

 Precondition:
  1. Technician is logged into the system.
  2. At least one active job card is assigned to the technician.
  3. Datastore contains valid service bookings linked to jobs.

 Steps:
  1. Navigate to Technician menu and choose "Complete Job".
    - Verify that the system lists active jobs with index-based selection.
  2. Select a job card by index.
    - Verify that the job status changes from STARTED to COMPLETED.
  3. Check customer view.
    - Verify that the updated status is reflected in the customer’s booking history.
  4. If all jobs in the booking are completed:
    - Verify that an invoice is generated and a notification is sent to the customer.
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
This commit is contained in:
Jissin Mathew
2026-05-20 12:05:39 +05:30
parent 56c5c2dc70
commit 8162a2fe3d
6 changed files with 175 additions and 11 deletions
@@ -1,4 +1,5 @@
#include "Controller.h" #include "Controller.h"
#include "JobCard.h"
bool Controller::login(const std::string& username, const std::string& password) bool Controller::login(const std::string& username, const std::string& password)
{ {
@@ -100,11 +101,20 @@ void Controller::removeService(const std::string& serviceID)
util::Map<std::string, const JobCard*> Controller::getJobCardsByUser() util::Map<std::string, const JobCard*> Controller::getJobCardsByUser()
{ {
return util::Map<std::string, const JobCard*>(); const User* currentUser = getAuthenticatedUser();
auto jobCardsAssignedToTechnician = m_serviceManagementService.getJobCards(currentUser->getId());
util::Map<std::string, const JobCard*> readOnlyJobCardMap;
for (int iterator = 0; iterator < jobCardsAssignedToTechnician.getSize(); iterator++)
{
JobCard* currentJobCard = jobCardsAssignedToTechnician.getValueAt(iterator);
readOnlyJobCardMap.insert(currentJobCard->getId(), currentJobCard);
}
return readOnlyJobCardMap;
} }
void Controller::completeJob(const std::string& jobID) void Controller::completeJob(const std::string& jobID)
{ {
m_serviceManagementService.completeJob(jobID);
} }
void Controller::removeUser(const std::string& userID) void Controller::removeUser(const std::string& userID)
@@ -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();
@@ -7,7 +7,7 @@ JobCard::JobCard()
m_booking(nullptr), m_booking(nullptr),
m_service(nullptr), m_service(nullptr),
m_technician(nullptr), m_technician(nullptr),
m_status(ServiceJobStatus()) {} m_status(util::ServiceJobStatus()) {}
JobCard::JobCard(const std::string& bookingId, JobCard::JobCard(const std::string& bookingId,
ServiceBooking* booking, ServiceBooking* booking,
@@ -16,7 +16,7 @@ JobCard::JobCard(const std::string& bookingId,
const std::string& technicianId, const std::string& technicianId,
User* technician, User* technician,
const util::Timestamp& assignedDate, const util::Timestamp& assignedDate,
ServiceJobStatus status, util::ServiceJobStatus status,
const util::Timestamp& completionDate const util::Timestamp& completionDate
) )
: m_id("JC" + std::to_string(++m_uid)), : m_id("JC" + std::to_string(++m_uid)),
@@ -70,7 +70,7 @@ const util::Timestamp& JobCard::getAssignedDate() const
return m_assignedDate; return m_assignedDate;
} }
ServiceJobStatus JobCard::getStatus() const util::ServiceJobStatus JobCard::getStatus() const
{ {
return m_status; return m_status;
} }
@@ -120,7 +120,7 @@ void JobCard::setAssignedDate(const util::Timestamp& assignedDate)
m_assignedDate = assignedDate; m_assignedDate = assignedDate;
} }
void JobCard::setStatus(ServiceJobStatus status) void JobCard::setStatus(util::ServiceJobStatus status)
{ {
m_status = status; m_status = status;
} }
@@ -1,13 +1,12 @@
#pragma once #pragma once
#include <string> #include <string>
#include "Timestamp.h" #include "Timestamp.h"
#include "Enums.h"
class ServiceBooking; class ServiceBooking;
class Service; class Service;
class User; class User;
enum class ServiceJobStatus : int;
class JobCard class JobCard
{ {
private: private:
@@ -20,7 +19,7 @@ private:
std::string m_technicianId; std::string m_technicianId;
User* m_technician; User* m_technician;
util::Timestamp m_assignedDate; util::Timestamp m_assignedDate;
ServiceJobStatus m_status; util::ServiceJobStatus m_status;
util::Timestamp m_completionDate; util::Timestamp m_completionDate;
public: public:
@@ -32,7 +31,7 @@ public:
const std::string& technicianId, const std::string& technicianId,
User* technician, User* technician,
const util::Timestamp& assignedDate, const util::Timestamp& assignedDate,
ServiceJobStatus status, util::ServiceJobStatus status,
const util::Timestamp& completionDate const util::Timestamp& completionDate
); );
const std::string& getId() const; const std::string& getId() const;
@@ -43,7 +42,7 @@ public:
const std::string& getTechnicianId() const; const std::string& getTechnicianId() const;
User* getTechnician() const; User* getTechnician() const;
const util::Timestamp& getAssignedDate() const; const util::Timestamp& getAssignedDate() const;
ServiceJobStatus getStatus() const; util::ServiceJobStatus getStatus() const;
const util::Timestamp& getCompletionDate() const; const util::Timestamp& getCompletionDate() const;
void setId(const std::string& id); void setId(const std::string& id);
void setBookingId(const std::string& bookingId); void setBookingId(const std::string& bookingId);
@@ -53,6 +52,6 @@ public:
void setTechnicianId(const std::string& technicianId); void setTechnicianId(const std::string& technicianId);
void setTechnician(User* technician); void setTechnician(User* technician);
void setAssignedDate(const util::Timestamp& assignedDate); void setAssignedDate(const util::Timestamp& assignedDate);
void setStatus(ServiceJobStatus status); void setStatus(util::ServiceJobStatus status);
void setCompletionDate(const util::Timestamp& completionDate); void setCompletionDate(const util::Timestamp& completionDate);
}; };
@@ -1 +1,89 @@
#include "ServiceManagementService.h" #include "ServiceManagementService.h"
#include "AuthenticationManagementService.h"
#include "PaymentManagementService.h"
#include "NotificationManagementService.h"
#include "JobCard.h"
#include "User.h"
#include "Enums.h"
#include "ServiceBooking.h"
#include "Service.h"
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;
}
static bool hasAllJobCardsinServiceBookingCompleted(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::STARTED)
{
return false;
}
}
}
return true;
}
void ServiceManagementService::completeJob(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::COMPLETED);
jobStatusUpdated = true;
}
}
else
{
throw std::runtime_error("Failed to complete the job, some error occured or job already completed.");
}
if (!jobStatusUpdated)
{
throw std::runtime_error("Failed to complete the job, some error occured or job already completed.");
}
serviceBookingCompleted = hasAllJobCardsinServiceBookingCompleted(currentJob->getBookingId(), currentAssignedJobs);
if (serviceBookingCompleted)
{
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed,Invoice Generated.\n";
std::string message = "Services completed for the booking and invoice generated.\n";
sendNotification(currentJob->getBooking()->getCustomer(), title, message);
}
}
@@ -1,6 +1,10 @@
#include <iomanip>
#include "TechnicianMenu.h" #include "TechnicianMenu.h"
#include "InputHelper.h" #include "InputHelper.h"
#include "OutputHelper.h" #include "OutputHelper.h"
#include "JobCard.h"
#include "Enums.h"
#include "Service.h"
void TechnicianMenu::showMenu() void TechnicianMenu::showMenu()
{ {
@@ -31,8 +35,68 @@ bool TechnicianMenu::handleOperation(int choice)
return false; return false;
} }
static std::string selectJobCardToComplete(util::Map<std::string, const JobCard*>& assignedJobCards, util::Map<int, const JobCard*>& incompleteJobCards)
{
int currentIndex = 1;
int choice;
bool hasIncompleteJobCard = false;
std::cout << std::left
<< std::setw(6) << "Index"
<< std::setw(12) << "BookingID"
<< std::setw(12) << "JobID"
<< std::setw(20) << "ServiceName"
<< std::setw(12) << "ServiceID"
<< std::endl;
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && (currentJobCard->getStatus() == util::ServiceJobStatus::STARTED))
{
std::cout << std::left << std::setw(6) << currentIndex
<< std::setw(12) << currentJobCard->getBookingId()
<< std::setw(12) << currentJobCard->getId()
<< std::setw(20) << currentJobCard->getService()->getName()
<< std::setw(12) << currentJobCard->getServiceId()
<< std::endl;
hasIncompleteJobCard = true;
incompleteJobCards.insert(currentIndex++, currentJobCard);
}
}
if (!hasIncompleteJobCard)
{
std::cout << "No pending jobs are present.\n";
return "";
}
std::cout << "Select the Job Card to complete (Index): ";
util::read(choice);
int selectedJobCardIndex = incompleteJobCards.find(choice);
if (selectedJobCardIndex != -1)
{
const JobCard* selectedJobCard = incompleteJobCards.getValueAt(selectedJobCardIndex);
return selectedJobCard->getId();
}
else
{
std::cout << "Invalid choice.\n";
return "";
}
}
void TechnicianMenu::completeJob() void TechnicianMenu::completeJob()
{ {
util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser();
util::Map<int, const JobCard*> incompleteJobCards;
std::cout << "Jobs to be completed.\n";
std::string selectedJobID = selectJobCardToComplete(assignedJobCards, incompleteJobCards);
if (selectedJobID == "")
{
std::cout << "Failed to complete the job.\n";
}
else
{
m_controller.completeJob(selectedJobID);
std::cout << "Job marked as completed.\n";
}
} }
void TechnicianMenu::viewNotifications() void TechnicianMenu::viewNotifications()