From 9b7d9cf7c19798aa2bd26baebde4e416b27ad7c4 Mon Sep 17 00:00:00 2001 From: Jissin Sam Mathew Date: Tue, 19 May 2026 18:47:12 +0530 Subject: [PATCH] Implement Assign Job to Technician functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SER001: Assign job to technician 1. Added ServiceManagementService logic to retrieve service bookings and create job cards for assigned technicians. 2. Added UserManagementService support to retrieve technicians by user type and fetch technician details by ID. 3. Connected Controller methods with ServiceManagementService and UserManagementService for service booking retrieval, technician listing, and job card creation. 4. Updated JobCard model to use util::ServiceJobStatus consistently and simplified constructor initialization for assigned and completion timestamps. 5. Added PENDING status in ServiceJobStatus enum for identifying unassigned service bookings. 6. Implemented AdminMenu job assignment flow to list pending service bookings, display available technicians, allow technician selection, and assign jobs for services in the booking. 7. Added notification trigger during job card creation for assigned technicians. Job assignment functionality validation Precondition: 1. System is running. 2. Pending service bookings are available in the system. 3. Technician users are available in the system. 4. Admin user is logged in and admin menu is active. Steps: 1. Launch the application and log in as an admin. 2. Select the Assign Job option from the admin menu. 3. View the list of available pending service bookings. - Verify that pending bookings are displayed. 4. Select a valid service booking. 5. View the list of available technicians. - Verify that technicians are listed for selection. 6. Select a technician to assign the job. - Verify that job cards are created for services in the booking. - Verify that assigned jobs are visible in the technician’s menu. - Verify that a confirmation message is shown. - Verify that a confirmation notification is sent to the assigned technician. Sreeja Reghukumar, please review --- .../controllers/Controller.cpp | 17 ++- .../controllers/Controller.h | 5 + .../models/JobCard.cpp | 14 +- .../models/JobCard.h | 13 +- .../models/ServiceBooking.cpp | 6 +- .../models/ServiceBooking.h | 8 +- .../services/ServiceManagementService.cpp | 69 +++++++++ .../services/ServiceManagementService.h | 1 + .../services/UserManagementService.cpp | 30 ++++ .../utilities/Enums.h | 3 + .../views/AdminMenu.cpp | 143 ++++++++++++++++++ 11 files changed, 284 insertions(+), 25 deletions(-) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp index d536e8a..7faa752 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.cpp @@ -68,7 +68,13 @@ void Controller::removeInventoryItem(const std::string& inventoryItemID) util::Map Controller::getServiceBookings() { - return util::Map(); + auto serviceBookings = m_serviceManagementService.getServiceBookings(); + util::Map readOnlyServiceBookings; + for (int iterator = 0; iterator < serviceBookings.getSize(); iterator++) + { + readOnlyServiceBookings.insert(serviceBookings.getKeyAt(iterator), serviceBookings.getValueAt(iterator)); + } + return readOnlyServiceBookings; } util::Map Controller::getServiceBookingsByUser(const std::string userID) @@ -83,11 +89,18 @@ util::Map Controller::getUsers() util::Map Controller::getUsers(util::UserType userType) { - return util::Map(); + auto userMap = m_userManagementService.getUsers(userType); + util::Map readOnlyUserMap; + for (int iterator = 0; iterator < userMap.getSize(); iterator++) + { + readOnlyUserMap.insert(userMap.getKeyAt(iterator), userMap.getValueAt(iterator)); + } + return readOnlyUserMap; } void Controller::createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID) { + m_serviceManagementService.createJobCard(bookingID, technicianID, serviceID); } void Controller::createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost) diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h index 3aabb58..b22a9b6 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/controllers/Controller.h @@ -2,6 +2,8 @@ #include "Map.h" #include #include "Enums.h" +#include "ServiceManagementService.h" +#include "UserManagementService.h" class Service; class ComboPackage; @@ -14,6 +16,9 @@ class Notification; class Controller { +private: + ServiceManagementService m_serviceManagementService; + UserManagementService m_userManagementService; public: bool login(const std::string& username, const std::string& password); void logout(); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp index 04e9195..0229352 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.cpp @@ -7,7 +7,7 @@ JobCard::JobCard() m_booking(nullptr), m_service(nullptr), m_technician(nullptr), - m_status(ServiceJobStatus()) {} + m_status(util::ServiceJobStatus()) {} JobCard::JobCard(const std::string& bookingId, ServiceBooking* booking, @@ -15,9 +15,7 @@ JobCard::JobCard(const std::string& bookingId, const std::string& serviceId, const std::string& technicianId, User* technician, - const util::Timestamp& assignedDate, - ServiceJobStatus status, - const util::Timestamp& completionDate + util::ServiceJobStatus status ) : m_id("JC" + std::to_string(++m_uid)), m_bookingId(bookingId), @@ -26,9 +24,9 @@ JobCard::JobCard(const std::string& bookingId, m_serviceId(serviceId), m_technicianId(technicianId), m_technician(technician), - m_assignedDate(assignedDate), + m_assignedDate(util::Timestamp()), m_status(status), - m_completionDate(completionDate) {} + m_completionDate(util::Timestamp()) {} const std::string& JobCard::getId() const { @@ -70,7 +68,7 @@ const util::Timestamp& JobCard::getAssignedDate() const return m_assignedDate; } -ServiceJobStatus JobCard::getStatus() const +util::ServiceJobStatus JobCard::getStatus() const { return m_status; } @@ -120,7 +118,7 @@ void JobCard::setAssignedDate(const util::Timestamp& assignedDate) m_assignedDate = assignedDate; } -void JobCard::setStatus(ServiceJobStatus status) +void JobCard::setStatus(util::ServiceJobStatus status) { m_status = status; } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h index 15a8a5d..c501e93 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/JobCard.h @@ -1,13 +1,12 @@ #pragma once #include #include "Timestamp.h" +#include "Enums.h" class ServiceBooking; class Service; class User; -enum class ServiceJobStatus : int; - class JobCard { private: @@ -20,7 +19,7 @@ private: std::string m_technicianId; User* m_technician; util::Timestamp m_assignedDate; - ServiceJobStatus m_status; + util::ServiceJobStatus m_status; util::Timestamp m_completionDate; public: @@ -31,9 +30,7 @@ public: const std::string& serviceId, const std::string& technicianId, User* technician, - const util::Timestamp& assignedDate, - ServiceJobStatus status, - const util::Timestamp& completionDate + util::ServiceJobStatus status ); const std::string& getId() const; const std::string& getBookingId() const; @@ -43,7 +40,7 @@ public: const std::string& getTechnicianId() const; User* getTechnician() const; const util::Timestamp& getAssignedDate() const; - ServiceJobStatus getStatus() const; + util::ServiceJobStatus getStatus() const; const util::Timestamp& getCompletionDate() const; void setId(const std::string& id); void setBookingId(const std::string& bookingId); @@ -53,6 +50,6 @@ public: void setTechnicianId(const std::string& technicianId); void setTechnician(User* technician); void setAssignedDate(const util::Timestamp& assignedDate); - void setStatus(ServiceJobStatus status); + void setStatus(util::ServiceJobStatus status); void setCompletionDate(const util::Timestamp& completionDate); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp index 1fdfaf0..aeb7f4d 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.cpp @@ -18,7 +18,7 @@ ServiceBooking::ServiceBooking( const std::string& vehicleBrand, const std::string& vehicleModel, const std::string& assignedTechnicianId, - const std::string& assignedTechnician, + const User* assignedTechnician, double discountPercentage ) : m_id("SRV" + std::to_string(++m_uid)), @@ -80,7 +80,7 @@ const std::string& ServiceBooking::getAssignedTechnicianId() const return m_assignedTechnicianId; } -const std::string& ServiceBooking::getAssignedTechnician() const +const User* ServiceBooking::getAssignedTechnician() const { return m_assignedTechnician; } @@ -135,7 +135,7 @@ void ServiceBooking::setAssignedTechnicianId(const std::string& assignedTechnici m_assignedTechnicianId = assignedTechnicianId; } -void ServiceBooking::setAssignedTechnician(const std::string& assignedTechnician) +void ServiceBooking::setAssignedTechnician(const User* assignedTechnician) { m_assignedTechnician = assignedTechnician; } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h index 5ecc1b0..8996d36 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/models/ServiceBooking.h @@ -19,7 +19,7 @@ private: std::string m_vehicleBrand; std::string m_vehicleModel; std::string m_assignedTechnicianId; - std::string m_assignedTechnician; + const User* m_assignedTechnician; double m_discountPercentage; public: ServiceBooking(); @@ -34,7 +34,7 @@ public: const std::string& vehicleBrand, const std::string& vehicleModel, const std::string& assignedTechnicianId, - const std::string& assignedTechnician, + const User* assignedTechnician, double discountPercentage ); const std::string& getId() const; @@ -46,7 +46,7 @@ public: const std::string& getVehicleBrand() const; const std::string& getVehicleModel() const; const std::string& getAssignedTechnicianId() const; - const std::string& getAssignedTechnician() const; + const User* getAssignedTechnician() const; double getDiscountPercentage() const; void setId(const std::string& id); void setStatus(const util::ServiceJobStatus& status); @@ -57,6 +57,6 @@ public: void setVehicleBrand(const std::string& vehicleBrand); void setVehicleModel(const std::string& vehicleModel); void setAssignedTechnicianId(const std::string& assignedTechnicianId); - void setAssignedTechnician(const std::string& assignedTechnician); + void setAssignedTechnician(const User* assignedTechnician); void setDiscountPercentage(double discountPercentage); }; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp index 156c12b..0bc77b9 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.cpp @@ -1 +1,70 @@ #include "ServiceManagementService.h" +#include "UserManagementService.h" +#include "ServiceBooking.h" +#include "Factory.h" +#include "JobCard.h" +#include "Timestamp.h" +#include "Service.h" +#include "InventoryItem.h" + +util::Map ServiceManagementService::getServiceBookings() +{ + return m_dataStore.getServiceBookings(); +} + +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; +} + +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->getQuantity() == 0) + { + std::string errorMessage = "Failed to create job card, " + currentInventoryItem->getPartName() + " is out of stock."; + throw std::runtime_error(errorMessage); + } + else + { + int currentStockQuantity = currentInventoryItem->getQuantity(); + currentInventoryItem->setQuantity(currentStockQuantity - 1); + } + } + currentBooking->setAssignedTechnician(selectedTechnician); + currentBooking->setAssignedTechnicianId(selectedTechnician->getId()); + 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(bookingID, currentBooking, currentService, serviceID, technicianID, selectedTechnician, util::ServiceJobStatus::STARTED); + currentJobCards.insert(jobCard->getId(), jobCard); + sendNotification(selectedTechnician,title, message); +} \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h index 85e05ed..ab5723e 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/ServiceManagementService.h @@ -22,6 +22,7 @@ public: void purchaseComboPackage(const std::string& comboPackageID, const std::string& vehicleNumber, const std::string& vehicleBrand, const std::string& vehicleModel); util::Map getServiceBookings(); util::Map getServiceBookings(const std::string& customerID); + ServiceBooking* getServiceBooking(const std::string& serviceID); void createJobCard(const std::string& bookingID, const std::string& technicianID, const std::string& serviceID); void createService(const std::string& name, const util::Vector& inventoryItemIDs, double laborCost); void removeService(const std::string& serviceID); diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp index 2a5bd9e..4f8adb2 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/UserManagementService.cpp @@ -1 +1,31 @@ #include "UserManagementService.h" +#include "User.h" + +util::Map UserManagementService::getUsers(util::UserType type) +{ + util::Map& currentUsers = m_dataStore.getUsers(); + util::Map filteredUsersMap; + for (int iterator = 0; iterator < currentUsers.getSize(); iterator++) + { + User* currentUser = currentUsers.getValueAt(iterator); + if (currentUser->getUserType() == type) + { + filteredUsersMap.insert(currentUser->getId(), currentUser); + } + } + return filteredUsersMap; +} + +User* UserManagementService::getUser(const std::string& userID) +{ + util::Map& currentUsers = m_dataStore.getUsers(); + for (int iterator = 0; iterator < currentUsers.getSize(); iterator++) + { + User* currentUser = currentUsers.getValueAt(iterator); + if (currentUser->getId() == userID) + { + return currentUser; + } + } + return nullptr; +} diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h index 24bbdcd..5353467 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/utilities/Enums.h @@ -24,6 +24,7 @@ namespace util enum class ServiceJobStatus { + PENDING, STARTED, COMPLETED }; @@ -125,6 +126,8 @@ namespace util return "STARTED"; case ServiceJobStatus::COMPLETED: return "COMPLETED"; + case ServiceJobStatus::PENDING: + return "STARTED"; } throw std::invalid_argument("Invalid ServiceJobStatus"); } diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp index 0432f3c..e0319ff 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/views/AdminMenu.cpp @@ -1,6 +1,10 @@ +#include #include "AdminMenu.h" #include "InputHelper.h" #include "OutputHelper.h" +#include "ServiceBooking.h" +#include "Enums.h" +#include "Service.h" void AdminMenu::showMenu() { @@ -56,8 +60,147 @@ void AdminMenu::checkStockAvailability() { } +static bool listServiceBookings(util::Map& currentBookings, int& bookingsSize, util::Map& serviceBookingsMap) +{ + int currentIndex = 1; + bool hasPendingService = false; + std::cout << std::left + << std::setw(10) << "Index" + << std::setw(10) << "ID" + << std::setw(12) << "Status" + << std::setw(12) << "CustID" + << std::setw(20) << "Customer" + << std::setw(15) << "VehicleNo" + << std::setw(15) << "Brand" + << std::setw(15) << "Model" + << std::setw(20) << "Technician" + << std::setw(15) << "TechID" + << std::endl; + for (int iterator = 0; iterator < bookingsSize; iterator++) + { + const ServiceBooking* currentBooking = currentBookings.getValueAt(iterator); + if (currentBooking && currentBooking->getStatus() == util::ServiceJobStatus::PENDING) + { + hasPendingService = true; + std::cout << std::left + << std::setw(10) << currentIndex + << std::setw(10) << currentBooking->getId() + << std::setw(12) << util::getServiceJobStatusString(currentBooking->getStatus()) + << std::setw(12) << currentBooking->getCustomerId() + << std::setw(20) << currentBooking->getCustomer()->getName() + << std::setw(15) << currentBooking->getVehicleNumber() + << std::setw(15) << currentBooking->getVehicleBrand() + << std::setw(15) << currentBooking->getVehicleModel() + << std::setw(20) << (currentBooking->getAssignedTechnician() == nullptr ? "Null" : currentBooking->getAssignedTechnician()->getName()) + << std::setw(15) << (currentBooking->getAssignedTechnicianId().empty() ? "Null" : currentBooking->getAssignedTechnicianId()) + << std::endl; + serviceBookingsMap.insert(currentIndex++, currentBooking); + } + } + if (!hasPendingService) + { + std::cout << "No pending service available." << std::endl; + return false; + } + return true; +} + +static const ServiceBooking* selectPendingServiceBookings(util::Map& serviceBookingsMap) +{ + int userInputIndex; + std::cout << "Enter a valid service index: "; + util::read(userInputIndex); + if (serviceBookingsMap.find(userInputIndex) != -1) + { + return serviceBookingsMap.getValueAt(userInputIndex); + } + else + { + std::cout << "Enter a valid index."; + return nullptr; + } +} + +static void listAvailableTechnicians( util::Map currentAvailableTechnicians, int numberOfTechnicians, util::Map& currentAvailableTechniciansMap) +{ + bool hasTechnicians = false; + int currentIndex = 1; + std::cout << std::left + << std::setw(6) << "Index" + << std::setw(15) << "Technician ID" + << std::setw(20) << "Name" + << std::endl; + for (int iterator = 0; iterator < numberOfTechnicians; iterator++) + { + const User* currentTechnician = currentAvailableTechnicians.getValueAt(iterator); + if (currentTechnician->getState() == util::State::INACTIVE) + { + continue; + } + hasTechnicians = true; + std::cout << std::left + << std::setw(6) << currentIndex + << std::setw(15) << currentTechnician->getId() + << std::setw(20) << currentTechnician->getName() + << std::endl; + currentAvailableTechniciansMap.insert(currentIndex++, currentTechnician); + } + if (!hasTechnicians) + { + std::cout << "No technicians currently available."; + } +} + +static const User* selectTechnician(util::Map& currentAvailableTechniciansMap) +{ + int userInputIndex; + util::read(userInputIndex); + if (currentAvailableTechniciansMap.find(userInputIndex) != -1) + { + return currentAvailableTechniciansMap.getValueAt(userInputIndex); + } + else + { + std::cout << "Enter a valid index."; + return nullptr; + } +} + void AdminMenu::assignJob() { + util::clear(); + std::string selectedService; + bool hasPendingService = false; + auto currentBookings = m_controller.getServiceBookings(); + auto availableTechnicians = m_controller.getUsers(util::UserType::TECHNICIAN); + int bookingsSize = currentBookings.getSize(); + util::Map serviceBookingsMap; + util::Map currentAvailableTechniciansMap; + if (listServiceBookings(currentBookings, bookingsSize, serviceBookingsMap)) + { + const ServiceBooking* selectedService = selectPendingServiceBookings(serviceBookingsMap); + if (selectedService) + { + if (availableTechnicians.getSize() != 0) + { + listAvailableTechnicians(availableTechnicians, availableTechnicians.getSize(), currentAvailableTechniciansMap); + const User* selectedTechnician = selectTechnician(currentAvailableTechniciansMap); + if (selectedTechnician) + { + auto& servicesInBooking = selectedService->getServices(); + for (int iterator = 0; iterator < servicesInBooking.getSize(); iterator++) + { + m_controller.createJobCard(selectedService->getId(), selectedTechnician->getId(), servicesInBooking.getValueAt(iterator)->getId()); + } + } + } + else + { + std::cout << "No technicians are currently available."; + } + } + } + } void AdminMenu::createService()