Compare commits

..

20 Commits

Author SHA1 Message Date
joelthomastrenser 451085e9c2 Add display menus for users, services, combo packages and jobs
Changes:
- Added display options for users, services, combo packages and technician jobs
- Updated admin and technician menu options and navigation
- Added reusable helper functions for displaying data in tabular format
- Improved user and combo package listing displays
- Fixed minor validation and error message formatting issues
2026-05-28 16:36:34 +05:30
joelthomastrenser 4657d3e8d1 Merged PR 1139: Fix: create missing directories before file creation
Fix: create missing directories before file creation

Changes:
- Added ensureDirectoryExists() helper using _mkdir()
- Automatically create missing directories before file operations
- Added FileHelper include in FileManager
- Removed placeholder files/README.md

Fixes #1793

Related work items: #1646, #1793
2026-05-28 10:35:54 +05:30
joelthomastrenser b1b6125d88 Fix: create missing directories before file creation
Changes:
- Added ensureDirectoryExists() helper using _mkdir()
- Automatically create missing directories before file operations
- Added FileHelper include in FileManager
- Removed placeholder files/README.md

Fixes #1793
2026-05-28 10:32:36 +05:30
joelthomastrenser e739ec6ee2 Merged PR 1134: Fix Invoice Deserialization Failure for PaymentMode::NOTSET
Changes:

- Added support for PaymentMode::NOTSET in payment mode string-to-enum conversion.
- Improved invoice table column labels and spacing for better readability in invoice display screens.

Fixes #1789

Related work items: #1646, #1789
2026-05-27 19:51:59 +05:30
joelthomastrenser f78e02ed3d Fix: handle NOTSET payment mode during invoice deserialization
Changes:

- Added support for PaymentMode::NOTSET in string-to-enum conversion
  to prevent invoice deserialization failures during system startup
- Improved invoice table column labels and spacing for better readability
  and alignment in invoice display screens

Fixes #1789
2026-05-27 18:47:49 +05:30
Avinash Rajesh 807490443e Merged PR 1128: Fix customer removal flow, admin inventory UI issues, and user display improvements
Changes:

- Refactored customer and technician removal flow:
  - Ensured linked job cards and service bookings are properly cancelled.
  - Centralized cancellation logic with processBookingCancellation helper for consistent notifications, technician reassignment, and inventory restoration.
  - Prevented duplicate inventory restocking during customer removal.
  - Preserved customer references and IDs correctly during booking cancellation.

- Admin inventory management improvements:
  - Added heading output to Remove Inventory Item submenu for clearer context.
  - Prevented service creation with empty inventory selection by adding validation and early return.
  - Improved combo package creation by simplifying null checks with modern idioms.
  - Enhanced inventory selection flow:
    - Clear screen and context header for better UX.
    - Prevent duplicate item selection by checking already chosen items.
    - Changed exit option from -1 to 0 for consistency.
    - Added pressEnter prompt after successful item addition.
  - Simplified pointer checks using concise conditions (e.g., if (item)).

- User management UI fix:
  - Updated active user display in Remove User menu to include Full Name column, ensuring administrators have complete visibility when managing users.

Fixes #1788
Fixes #1778
Fixes #1781

Related work items: #1778, #1781, #1788
2026-05-27 18:38:17 +05:30
Avinash Rajesh b7bc1f574d Implement Review Fixes 2026-05-27 17:32:36 +05:30
Avinash Rajesh b25b3d59cf Fix Admin Menu Remove User Incomplete Data
- Updated active user display to include Full Name column.
- Ensured both header and row output show full name alongside ID, username, and user type.
- Improved clarity of Remove User submenu by presenting complete user information.

Fixes #1788
2026-05-27 17:12:23 +05:30
Avinash Rajesh c1bd2a6ef1 Fix Admin Inventory Management UI Issues and Workflow Enhancements
- Added heading output to Remove Inventory Item submenu for clearer user context.
- Prevented service creation with empty inventory selection by adding validation and early return.
- Improved combo package creation by simplifying null checks with modern idioms.
- Enhanced inventory selection flow:
  - Clear screen and context header for better UX.
  - Prevent duplicate item selection by checking already chosen items.
  - Changed exit option from -1 to 0 for consistency.
  - Added pressEnter prompt after successful item addition.
- Simplified pointer checks using concise conditions (e.g., if (item)).

Fixes #1778
2026-05-27 17:12:23 +05:30
Avinash Rajesh 859f7bbeaa Fix Customer Removal and Cancellation Handling Issues
- Refactored customer and technician removal flow to ensure linked job cards and service bookings are properly cancelled.
- Added inventory restoration logic to avoid duplicate restocking when cancelling bookings.
- Introduced processBookingCancellation helper for consistent cancellation handling, notifications, and technician reassignment.
- Updated UserManagementService::removeUser to invoke appropriate cancellation routines based on user type.
- Ensured customer references and IDs are preserved correctly during booking cancellation.

Fixes #1781
2026-05-27 17:12:20 +05:30
Jissin Mathew d6cc6fc04f Merged PR 1129: Fix view invoices, combo packages, and technician menu UI issues
Changes
- Added tabular invoice list with selection before full details
- Displayed Payment Mode in detailed invoice view
- Removed duplicate pressEnter() calls and improved console messages
- Changed combo package bookings to start with PENDING status
- Added filterComboPackages to list only active packages
- Improved CustomerMenu combo package selection with clearer messages and formatting
- Enhanced output with console clearing, spacing, and success feedback
- Added truncateString utility for consistent display of long names
- Updated filter functions to use reference parameters for efficiency
- Cleared console before displaying Complete Job screen
- Improved submenu header and formatting for clarity
- Prevented table headers from showing when no jobs exist
- Refined job completion flow with consistent messages

Fixes #1779
Fixes #1782
Fixes #1784

Related work items: #1779, #1782, #1784
2026-05-27 17:09:18 +05:30
Jissin Mathew 62cd7dcc89 Implement Review Fixes 2026-05-27 17:09:03 +05:30
Jissin Mathew c67bf962c1 Fix Customer Menu View Invoices UI and Navigation Issues
- Added tabular invoice list for better navigation
- Enabled user to select an invoice before viewing full details
- Displayed Payment Mode in detailed invoice view
- Removed duplicate pressEnter() calls
- Improved console messages and formatting for clarity

Fixes #1784
2026-05-27 16:55:14 +05:30
Jissin Mathew efd8b32a5f Fix Combo Package Status and Creation Flow Issues
- Changed combo package bookings to start with PENDING status
- Added filterComboPackages to list only active packages
- Improved CustomerMenu combo package selection with clearer messages and formatting
- Enhanced output with console clearing, spacing, and success feedback
- Added truncateString utility for consistent display of long names
- Updated filter functions to use reference parameters for efficiency

Fixes #1779
2026-05-27 16:53:30 +05:30
Jissin Mathew 01596e825e Fix Technician Menu Complete Job UI Issues
- Cleared console before displaying Complete Job screen
- Improved submenu header and formatting for clarity
- Prevented table headers from showing when no jobs exist
- Refined job completion flow with consistent messages

Fixes #1782
2026-05-27 16:53:29 +05:30
joelthomastrenser fb50aeba47 Merged PR 1127: Fix notification UI formatting, payment flow issues, and observer cleanup
Changes:

- Fix notification table alignment issues in admin notification screens
- Add proper spacing for notification title column display
- Remove unnecessary tab spacing from Configure Notification Preferences heading
- Change ServiceBooking ID prefix to avoid conflict with Service IDs
- Remove unnecessary newline characters from service booking completion notifications
- Detach removed users from all service observer lists during user removal
- Fix Complete Payments screen clearing immediately after heading display
- Improve table spacing in Complete Payments screen
- Prevent invalid payment mode selection from defaulting to OFFLINE mode

Fixes #1780
Fixes #1783
Fixes #1777
Fixes #1786

Related work items: #1777, #1780, #1783, #1786
2026-05-27 16:49:20 +05:30
joelthomastrenser defee9aa15 Fix ServiceBooking ID prefix and notification messages
- change ServiceBooking ID prefix from `SRV` to `SBK`
- add customer notification when technician is assigned to booking
- fix typo in job completion exception message
- remove unnecessary newline characters from completion notifications

Fixes #1780
2026-05-27 16:02:23 +05:30
joelthomastrenser 67e5917a57 Fix: improve complete payments table formatting and payment mode validation
- Increased spacing between invoice table columns for better readability
- Updated invoice table headers with clearer labels
- Prevented screen clear before invoice listing display
- Changed payment mode selection to re-prompt on invalid input instead of defaulting to offline mode
- Removed unnecessary blank line before service listing display

Fixes #1786
2026-05-27 13:02:40 +05:30
joelthomastrenser 83e2bed432 Fix: improve notification display formatting and add global exception handling
- add truncateString utility for long notification titles
- improve notification table alignment
- remove unnecessary heading spacing in notification preferences
- add application-level exception handling in UserInterface::run()

Fixes #1777
2026-05-27 12:19:34 +05:30
joelthomastrenser a9c8ec93b7 Fix: detach removed users from service observers
- detach users from InventoryManagementService observers during removal
- detach users from PaymentManagementService observers during removal
- detach users from ServiceManagementService observers during removal

Fixes #1783
2026-05-27 11:10:59 +05:30
17 changed files with 822 additions and 281 deletions
@@ -409,10 +409,8 @@ void Controller::removeUser(const std::string& userID)
User* user = m_userManagementService.getUser(userID); User* user = m_userManagementService.getUser(userID);
if (!user) if (!user)
{ {
throw std::runtime_error("Error User not Found.\n"); throw std::runtime_error("Error: User not Found.\n");
} }
m_serviceManagementService.cancelCustomerServiceBookings(userID);
m_serviceManagementService.cancelTechnicianJobs(userID);
m_userManagementService.removeUser(userID); m_userManagementService.removeUser(userID);
} }
@@ -1 +0,0 @@
Place files here.
@@ -24,9 +24,10 @@ Parameters: None
Returns: A new ServiceBooking object. Returns: A new ServiceBooking object.
*/ */
ServiceBooking::ServiceBooking() ServiceBooking::ServiceBooking()
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SBK" + std::to_string(++m_uid)),
m_customer(nullptr), m_customer(nullptr),
m_assignedTechnician(nullptr), m_assignedTechnician(nullptr),
m_status(util::ServiceJobStatus::PENDING),
m_discountPercentage(0.0) {} m_discountPercentage(0.0) {}
/* /*
@@ -56,7 +57,7 @@ ServiceBooking::ServiceBooking(
const std::string& vehicleModel, const std::string& vehicleModel,
double discountPercentage double discountPercentage
) )
: m_id("SRV" + std::to_string(++m_uid)), : m_id("SBK" + std::to_string(++m_uid)),
m_status(status), m_status(status),
m_services(services), m_services(services),
m_customerId(customerId), m_customerId(customerId),
@@ -294,7 +294,7 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
{ {
throw std::runtime_error("Invoice generation failed: booking is null."); throw std::runtime_error("Invoice generation failed: booking is null.");
} }
double totalLabourCost = 0, totalPartsCost = 0, totalServiceCost = 0; double totalLaborCost = 0, totalPartsCost = 0, totalServiceCost = 0;
double discountPercentage = booking->getDiscountPercentage(); double discountPercentage = booking->getDiscountPercentage();
std::string bookingID = booking->getId(); std::string bookingID = booking->getId();
util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices(); util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices();
@@ -303,9 +303,10 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++) for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++)
{ {
JobCard* currentJobCard = currentJobCards.getValueAt(iterator); JobCard* currentJobCard = currentJobCards.getValueAt(iterator);
if (currentJobCard->getBookingId() == bookingID && currentJobCard->getStatus() != util::ServiceJobStatus::COMPLETED) util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus();
if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED)
{ {
throw std::runtime_error("Invoice generation failed: not all job cards are completed for booking '" + bookingID + "'."); throw std::runtime_error("Invoice generation failed: Not all job cards are completed for booking '" + bookingID + "'.");
} }
} }
for (int iterator = 0; iterator < servicesInTheBookedService.getSize(); iterator++) for (int iterator = 0; iterator < servicesInTheBookedService.getSize(); iterator++)
@@ -314,13 +315,13 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
if (currentService) if (currentService)
{ {
createInventoryItemsMap(completeInventoryItemMapOfBooking, currentService); createInventoryItemsMap(completeInventoryItemMapOfBooking, currentService);
totalLabourCost += currentService->getLaborCost(); totalLaborCost += currentService->getLaborCost();
totalPartsCost += util::calculatePartsCost(currentService); totalPartsCost += util::calculatePartsCost(currentService);
} }
} }
totalServiceCost = totalLabourCost + totalPartsCost; totalServiceCost = totalLaborCost + totalPartsCost;
totalServiceCost -= (totalServiceCost * (discountPercentage / 100)); totalServiceCost -= (totalServiceCost * (discountPercentage / 100));
Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLabourCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING); Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLaborCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING);
util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices(); util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices();
currentInvoices.insert(invoice->getId(), invoice); currentInvoices.insert(invoice->getId(), invoice);
} }
@@ -375,7 +376,7 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
invoice->setStatus(util::PaymentStatus::COMPLETED); invoice->setStatus(util::PaymentStatus::COMPLETED);
std::string title, message; std::string title, message;
title = "Payment successful"; title = "Payment successful";
message = "Payment successful for invoice ID " + invoiceID; message = "Payment successful for Invoice ID " + invoiceID;
sendNotification(currentUser, title, message); sendNotification(currentUser, title, message);
} }
} }
@@ -26,7 +26,6 @@ Date:19-May-2026
#include "User.h" #include "User.h"
#include "UserManagementService.h" #include "UserManagementService.h"
#include "Utility.h" #include "Utility.h"
#include "ComboPackage.h"
/* /*
Function: purchaseService Function: purchaseService
@@ -100,7 +99,7 @@ void ServiceManagementService::purchaseComboPackage(const std::string& comboPack
} }
const ComboPackage* comboPackage = comboPackagesMap[comboPackageID]; const ComboPackage* comboPackage = comboPackagesMap[comboPackageID];
util::Map<std::string, Service*> selectedServices = comboPackage->getServices(); util::Map<std::string, Service*> selectedServices = comboPackage->getServices();
ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::STARTED, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage()); ServiceBooking* serviceBooking = Factory::getObject<ServiceBooking>(util::ServiceJobStatus::PENDING, selectedServices, authenticatedUser->getId(), authenticatedUser, vehicleNumber, vehicleBrand, vehicleModel, comboPackage->getDiscountPercentage());
if (serviceBooking == nullptr) if (serviceBooking == nullptr)
{ {
throw std::runtime_error("Failed to create combo package service booking"); throw std::runtime_error("Failed to create combo package service booking");
@@ -508,6 +507,89 @@ void ServiceManagementService::saveObservers()
util::saveObservers(config::file::SERVICEMANAGEMENTOBSERVERS, this); 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 Function: cancelCustomerServiceBookings
Description: Cancels all service bookings associated with a given customer or technician. Description: Cancels all service bookings associated with a given customer or technician.
@@ -516,69 +598,47 @@ Description: Cancels all service bookings associated with a given customer or te
Parameter: const std::string& userID - ID of the customer or technician Parameter: const std::string& userID - ID of the customer or technician
Return type: void Return type: void
*/ */
void ServiceManagementService::cancelCustomerServiceBookings(const std::string& userID) void ServiceManagementService::cancelCustomerServiceBookings(const std::string& customerID)
{ {
const int INCREMENT_VALUE = 1;
auto& users = m_dataStore.getUsers(); auto& users = m_dataStore.getUsers();
int userIndex = users.find(userID); int userIndex = users.find(customerID);
if (userIndex == -1) if (userIndex == -1)
{ {
throw std::runtime_error("User not found: " + userID); throw std::runtime_error("User not found: " + customerID);
} }
User* user = users.getValueAt(userIndex); User* customer = users.getValueAt(userIndex);
if (user == nullptr) if (!customer)
{ {
throw std::runtime_error("User not found: " + userID); throw std::runtime_error("User not found: " + customerID);
} }
util::UserType type = user->getUserType(); auto& bookings = m_dataStore.getServiceBookings();
auto& bookings = DataStore::getInstance().getServiceBookings(); auto& jobs = m_dataStore.getJobCards();
for (int bookingIterator = 0; bookingIterator < bookings.getSize(); bookingIterator++) for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
{ {
ServiceBooking* booking = bookings.getValueAt(bookingIterator); ServiceBooking* booking = bookings.getValueAt(iteratorOne);
if (booking != nullptr && if (!booking)
(booking->getCustomerId() == userID || booking->getAssignedTechnicianId() == userID))
{ {
if (booking->getStatus() == util::ServiceJobStatus::PENDING || continue;
booking->getStatus() == util::ServiceJobStatus::STARTED) }
if (booking->getCustomerId() != customerID)
{ {
if (type == util::UserType::CUSTOMER) continue;
}
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
{ {
booking->setStatus(util::ServiceJobStatus::CANCELLED); continue;
booking->setCustomer(nullptr); }
booking->setCustomerId("");
User* assignedTechnician = booking->getAssignedTechnician(); User* assignedTechnician = booking->getAssignedTechnician();
std::string title = "Customer Service Cancelled"; std::string titleToTechnician = "Customer Service Cancelled";
std::string message = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked."; std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked.";
sendNotification(assignedTechnician, title, message); 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.";
else if (type == util::UserType::TECHNICIAN) processBookingCancellation(booking,
{ util::ServiceJobStatus::CANCELLED,
booking->setStatus(util::ServiceJobStatus::PENDING); titleToTechnician, messageToTechnician, assignedTechnician,
std::string title = "Technician Unavailable"; util::ServiceJobStatus::CANCELLED,
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."; jobTitle, jobMessage, jobs, *this
sendNotification(booking->getCustomer(), title, message); );
}
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
const auto& ListOfServices = booking->getServices();
for (int serviceIterator = 0; serviceIterator < ListOfServices.getSize(); serviceIterator++)
{
Service* service = ListOfServices.getValueAt(serviceIterator);
if (service != nullptr)
{
const auto& items = service->getRequiredInventoryItems();
for (int itemIterator = 0; itemIterator < items.getSize(); itemIterator++)
{
InventoryItem* item = items.getValueAt(itemIterator);
if (item != nullptr)
{
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
}
}
}
}
}
}
} }
} }
@@ -591,34 +651,48 @@ Return type: void
*/ */
void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID) void ServiceManagementService::cancelTechnicianJobs(const std::string& technicianID)
{ {
const int INCREMENT_VALUE = 1; 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(); auto& jobs = m_dataStore.getJobCards();
for (int jobIterator = 0; jobIterator < jobs.getSize(); jobIterator++) for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
{ {
JobCard* job = jobs.getValueAt(jobIterator); ServiceBooking* booking = bookings.getValueAt(iteratorOne);
if (job != nullptr && job->getTechnicianId() == technicianID) if (!booking)
{ {
if (job->getStatus() == util::ServiceJobStatus::PENDING || job->getStatus() == util::ServiceJobStatus::STARTED) continue;
{
job->setStatus(util::ServiceJobStatus::CANCELLED);
std::string title = "Job Cancelled";
std::string message = "The Job has cancelled. Your job card has been cancelled and the inventory has been restocked.";
sendNotification(job->getTechnician(), title, message);
Service* service = job->getService();
if (service != nullptr)
{
const auto& items = service->getRequiredInventoryItems();
for (int itemIterator = 0; itemIterator < items.getSize(); itemIterator++)
{
InventoryItem* item = items.getValueAt(itemIterator);
if (item != nullptr)
{
item->setQuantity(item->getQuantity() + INCREMENT_VALUE);
}
} }
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
);
} }
} }
@@ -833,6 +907,9 @@ void ServiceManagementService::createJobCard(const std::string& bookingID, const
{ {
throw std::runtime_error("Failed to create job card."); 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);
} }
/* /*
@@ -1055,11 +1132,11 @@ void ServiceManagementService::completeJob(const std::string& jobID)
} }
else else
{ {
throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); throw std::runtime_error("Failed to complete the job, some error occurred or job already completed.");
} }
if (!jobStatusUpdated) if (!jobStatusUpdated)
{ {
throw std::runtime_error("Failed to complete the job, some error occured or job already completed."); throw std::runtime_error("Failed to complete the job, some error occurred or job already completed.");
} }
serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs); serviceBookingCompleted = hasCompletedAllJobs(currentJob->getBookingId(), currentAssignedJobs);
@@ -1067,8 +1144,8 @@ void ServiceManagementService::completeJob(const std::string& jobID)
{ {
currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED); currentJob->getBooking()->setStatus(util::ServiceJobStatus::COMPLETED);
paymentManagementService.generateInvoice(currentJob->getBooking()); paymentManagementService.generateInvoice(currentJob->getBooking());
std::string title = "Service Booking completed,Invoice Generated.\n"; std::string title = "Service Booking completed. Invoice Generated.";
std::string message = "Services completed for the booking and invoice generated.\n"; std::string message = "Services completed for the booking and invoice generated.";
sendNotification(currentJob->getBooking()->getCustomer(), title, message); sendNotification(currentJob->getBooking()->getCustomer(), title, message);
} }
} }
@@ -297,13 +297,27 @@ Return type: void
*/ */
void UserManagementService::removeUser(const std::string& userID) void UserManagementService::removeUser(const std::string& userID)
{ {
InventoryManagementService inventoryManagementService;
PaymentManagementService paymentManagementService;
ServiceManagementService serviceManagementService;
int index = m_dataStore.getUsers().find(userID); int index = m_dataStore.getUsers().find(userID);
if (index != -1) if (index != -1)
{ {
User* user = m_dataStore.getUsers().getValueAt(index); User* user = m_dataStore.getUsers().getValueAt(index);
if (user != nullptr) if (user != nullptr)
{ {
if (user->getUserType() == util::UserType::CUSTOMER)
{
serviceManagementService.cancelCustomerServiceBookings(userID);
}
if (user->getUserType() == util::UserType::TECHNICIAN)
{
serviceManagementService.cancelTechnicianJobs(userID);
}
user->setState(util::State::INACTIVE); user->setState(util::State::INACTIVE);
inventoryManagementService.detach(user);
paymentManagementService.detach(user);
serviceManagementService.detach(user);
} }
} }
} }
@@ -137,6 +137,10 @@ namespace util
{ {
return PaymentMode::OFFLINE; return PaymentMode::OFFLINE;
} }
if (value == "NOTSET")
{
return PaymentMode::NOTSET;
}
throw std::invalid_argument("Invalid PaymentMode string"); throw std::invalid_argument("Invalid PaymentMode string");
} }
@@ -11,10 +11,39 @@ Date: 22-May-2026
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <direct.h>
#include "Vector.h" #include "Vector.h"
namespace util namespace util
{ {
/*
Function: ensureDirectoryExists
Description: Creates all missing directories present in the given file path.
Iteratively parses the path and creates each directory level
using _mkdir() before file operations are performed.
Parameters:
- filePath: const std::string&, relative or absolute file path
Returns:
- void
Throws:
- None (_mkdir failures are intentionally ignored if directory already exists)
*/
inline void ensureDirectoryExists(const std::string& filePath)
{
size_t position = 0;
while ((position = filePath.find('/', position)) != std::string::npos)
{
std::string directory = filePath.substr(0, position);
if (!directory.empty())
{
(void)_mkdir(directory.c_str());
}
position++;
}
}
/* /*
Function: loadRecords Function: loadRecords
Description: Loads records from a given file path into a vector of strings. Description: Loads records from a given file path into a vector of strings.
@@ -32,6 +61,7 @@ namespace util
std::ifstream file(filePath); std::ifstream file(filePath);
if (!file.is_open()) if (!file.is_open())
{ {
ensureDirectoryExists(filePath);
std::ofstream newFile(filePath); std::ofstream newFile(filePath);
newFile.close(); newFile.close();
file.open(filePath); file.open(filePath);
@@ -15,6 +15,7 @@ Date: 22-May-2026
#include <fstream> #include <fstream>
#include "Vector.h" #include "Vector.h"
#include "Map.h" #include "Map.h"
#include "FileHelper.h"
namespace util namespace util
{ {
@@ -51,6 +52,7 @@ namespace util
std::ifstream file(m_filePath); std::ifstream file(m_filePath);
if (!file.is_open()) if (!file.is_open())
{ {
ensureDirectoryExists(m_filePath);
std::ofstream newFile(m_filePath); std::ofstream newFile(m_filePath);
newFile.close(); newFile.close();
file.open(m_filePath); file.open(m_filePath);
@@ -21,4 +21,28 @@ namespace util
{ {
std::cout << "\x1B[2J\x1B[H" << std::flush; std::cout << "\x1B[2J\x1B[H" << std::flush;
} }
/*
Function: truncateString
Description:
Truncates a string if its length exceeds the given maximum length.
The truncated string ends with "..." to indicate omitted characters.
Parameters:
- text: const std::string&, input string to truncate
- maxLength: size_t, maximum allowed length of the returned string
Returns:
- std::string: Original string if within limit, otherwise truncated string with "..."
*/
inline std::string truncateString(const std::string& text, size_t maxLength)
{
if (text.length() <= maxLength)
{
return text;
}
if (maxLength <= 3)
{
return std::string(maxLength, '.');
}
return text.substr(0, maxLength - 3) + "...";
}
} }
@@ -42,15 +42,18 @@ void AdminMenu::showMenu()
<< "\n3. Remove Inventory Item" << "\n3. Remove Inventory Item"
<< "\n4. Check Stock Availability" << "\n4. Check Stock Availability"
<< "\n5. Assign Job to Technician" << "\n5. Assign Job to Technician"
<< "\n6. Add Technician" << "\n6. Display Users"
<< "\n7. Remove Customer/Technician" << "\n7. Add Technician"
<< "\n8. Create Service" << "\n8. Remove Customer/Technician"
<< "\n9. Remove Service" << "\n9. Display Services"
<< "\n10. Create Combo Package" << "\n10. Create Service"
<< "\n11. Remove Combo Package" << "\n11. Remove Service"
<< "\n12. View Notifications" << "\n12. Display Combo Packages"
<< "\n13. Change Password" << "\n13. Create Combo Package"
<< "\n14. Logout" << "\n14. Remove Combo Package"
<< "\n15. View Notifications"
<< "\n16. Change Password"
<< "\n17. Logout"
<< "\nEnter a choice: "; << "\nEnter a choice: ";
util::read(choice); util::read(choice);
if (!handleOperation(choice)) if (!handleOperation(choice))
@@ -92,30 +95,39 @@ bool AdminMenu::handleOperation(int choice)
assignJob(); assignJob();
break; break;
case 6: case 6:
addTechnician(); displayUsers();
break; break;
case 7: case 7:
removeUser(); addTechnician();
break; break;
case 8: case 8:
createService(); removeUser();
break; break;
case 9: case 9:
removeService(); displayServices();
break; break;
case 10: case 10:
createComboPackages(); createService();
break; break;
case 11: case 11:
removeComboPackage(); removeService();
break; break;
case 12: case 12:
viewNotifications(); displayComboPackages();
break; break;
case 13: case 13:
changePassword(); createComboPackages();
break; break;
case 14: case 14:
removeComboPackage();
break;
case 15:
viewNotifications();
break;
case 16:
changePassword();
break;
case 17:
logout(); logout();
return false; return false;
default: default:
@@ -263,6 +275,7 @@ Return type: void
void AdminMenu::removeInventoryItem() void AdminMenu::removeInventoryItem()
{ {
util::clear(); util::clear();
std::cout << "Remove Inventory Item\n";
auto inventoryItems = m_controller.getInventoryItems(); auto inventoryItems = m_controller.getInventoryItems();
auto activeItems = filterActiveItems(inventoryItems); auto activeItems = filterActiveItems(inventoryItems);
int activeItemsSize = activeItems.getSize(); int activeItemsSize = activeItems.getSize();
@@ -382,6 +395,24 @@ void AdminMenu::assignJob()
util::pressEnter(); util::pressEnter();
} }
/*
Function: displayServices()
Description: Display all active services
Parameters:
- None
Returns:
- void
*/
void AdminMenu::displayServices()
{
util::clear();
std::cout << "List of all Services\n";
util::Map<std::string, const Service*> currentServices = m_controller.getServices();
util::Map<std::string, const Service*> currentActiveServices = filterActiveServices(currentServices);
displayAllServices(currentActiveServices);
util::pressEnter();
}
/* /*
Function: createService Function: createService
Description: Allows the admin to create a new service by selecting inventory items and specifying labor cost. Description: Allows the admin to create a new service by selecting inventory items and specifying labor cost.
@@ -402,6 +433,11 @@ void AdminMenu::createService()
util::Map<std::string, const InventoryItem*> activeInventoryItems = filterActiveItems(currentInventoryItems); util::Map<std::string, const InventoryItem*> activeInventoryItems = filterActiveItems(currentInventoryItems);
util::Vector<std::string> selectedInventoryItems; util::Vector<std::string> selectedInventoryItems;
selectInventoryItems(activeInventoryItems,selectedInventoryItems); selectInventoryItems(activeInventoryItems,selectedInventoryItems);
if (selectedInventoryItems.isEmpty())
{
util::pressEnter();
return;
}
std::cout << "\nEnter the labour cost: "; std::cout << "\nEnter the labour cost: ";
util::read(labourCost); util::read(labourCost);
m_controller.createService(serviceName, selectedInventoryItems, labourCost); m_controller.createService(serviceName, selectedInventoryItems, labourCost);
@@ -428,7 +464,7 @@ void AdminMenu::removeService()
if (selectedServiceID != "") if (selectedServiceID != "")
{ {
m_controller.removeService(selectedServiceID); m_controller.removeService(selectedServiceID);
std::cout << "Service removed sucessfully.\n\n"; std::cout << "Service removed successfully.\n\n";
} }
else else
{ {
@@ -437,6 +473,29 @@ void AdminMenu::removeService()
util::pressEnter(); util::pressEnter();
} }
/*
Function: displayUsers
Description: Displays all users.
Parameter: None
Return type: void
*/
void AdminMenu::displayUsers()
{
util::clear();
auto listOfUsers = m_controller.getUsers();
auto listOfActiveUsers = filterActiveUsers(listOfUsers);
int activeUserCount = listOfActiveUsers.getSize();
std::cout << "List of all Users\n";
if (activeUserCount < 1)
{
std::cout << "No Active users." << std::endl;
util::pressEnter();
return;
}
displayAllUsers(listOfActiveUsers);
util::pressEnter();
}
/* /*
Function: addTechnician Function: addTechnician
Description: Adds a new technician after validating username, password, email, and phone number. Description: Adds a new technician after validating username, password, email, and phone number.
@@ -501,12 +560,12 @@ void AdminMenu::removeUser()
util::pressEnter(); util::pressEnter();
return; return;
} }
displayAllActiveUsers(listOfActiveUsers, activeUserCount); displayAllUsers(listOfActiveUsers);
std::cout << "Enter the index of the user to delete : "; std::cout << "Enter the index of the user to delete : ";
util::read(indexChoice); util::read(indexChoice);
if (indexChoice < 1 || indexChoice > activeUserCount) if (indexChoice < 1 || indexChoice > activeUserCount)
{ {
std::cout << "Error Invaild index.\n" << std::endl; std::cout << "Error invalid index.\n" << std::endl;
util::pressEnter(); util::pressEnter();
return; return;
} }
@@ -520,6 +579,24 @@ void AdminMenu::removeUser()
util::pressEnter(); util::pressEnter();
} }
/*
Function: displayComboPackages()
Description: Display all active combo packages
Parameters:
- None
Returns:
- void
*/
void AdminMenu::displayComboPackages()
{
util::clear();
std::cout << "List of all Combo Packages\n";
util::Map<std::string, const ComboPackage*> currentComboPackages = m_controller.getComboPackages();
util::Map<std::string, const ComboPackage*> currentActiveComboPackages = filterComboPackages(currentComboPackages);
displayAllComboPackages(currentActiveComboPackages);
util::pressEnter();
}
/* /*
Function: createComboPackages Function: createComboPackages
Description: Creates a new combo package by selecting two active services and applying a discount. Description: Creates a new combo package by selecting two active services and applying a discount.
@@ -541,7 +618,7 @@ void AdminMenu::createComboPackages()
while (true) while (true)
{ {
chosenService = selectServiceFromServices(activeServices); chosenService = selectServiceFromServices(activeServices);
if (chosenService == nullptr) if (!chosenService)
{ {
std::cout << "Failed to create combo package!\n\n"; std::cout << "Failed to create combo package!\n\n";
util::pressEnter(); util::pressEnter();
@@ -24,10 +24,13 @@ public:
void removeInventoryItem(); void removeInventoryItem();
void checkStockAvailability(); void checkStockAvailability();
void assignJob(); void assignJob();
void displayServices();
void createService(); void createService();
void removeService(); void removeService();
void displayUsers();
void addTechnician(); void addTechnician();
void removeUser(); void removeUser();
void displayComboPackages();
void createComboPackages(); void createComboPackages();
void removeComboPackage(); void removeComboPackage();
void viewNotifications(); void viewNotifications();
@@ -220,20 +220,24 @@ void CustomerMenu::selectComboPackage()
util::clear(); util::clear();
std::cout << "Select a Combo Package\n"; std::cout << "Select a Combo Package\n";
auto comboPackages = m_controller.getComboPackages(); auto comboPackages = m_controller.getComboPackages();
if (comboPackages.isEmpty()) util::Map<std::string, const ComboPackage*> activeComboPackages = filterComboPackages(comboPackages);
if (activeComboPackages.isEmpty())
{ {
std::cout << "No combo packages available!"; std::cout << "No combo packages available!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(comboPackages); const ComboPackage* selectedComboPackage = selectComboPackageFromPackages(activeComboPackages);
if (selectedComboPackage == nullptr) if (selectedComboPackage == nullptr)
{ {
std::cout << "Failed to book combo package!"; std::cout << "Failed to book combo package!\n\n";
util::pressEnter(); util::pressEnter();
return; return;
} }
std::cout << "Combo Package selected\n";
util::pressEnter();
util::clear(); util::clear();
std::cout << "Enter the vehicle details\n";
std::cout << "Enter vehicle number: "; std::cout << "Enter vehicle number: ";
util::read(vehicleNumber); util::read(vehicleNumber);
std::cout << "Enter vehicle brand: "; std::cout << "Enter vehicle brand: ";
@@ -241,7 +245,7 @@ void CustomerMenu::selectComboPackage()
std::cout << "Enter vehicle model: "; std::cout << "Enter vehicle model: ";
util::read(vehicleModel); util::read(vehicleModel);
m_controller.purchaseComboPackage(selectedComboPackage->getId(), vehicleNumber, vehicleBrand, vehicleModel); m_controller.purchaseComboPackage(selectedComboPackage->getId(), vehicleNumber, vehicleBrand, vehicleModel);
std::cout << "Combo Package has been booked successfully"; std::cout << "Combo Package has been booked successfully\n\n";
util::pressEnter(); util::pressEnter();
} }
@@ -29,6 +29,41 @@ Date: 21-May-2026
#include "Validator.h" #include "Validator.h"
#include "Vector.h" #include "Vector.h"
/*
Function: displayAllServices
Description: Displays all active services
Parameters:
- currentServices: util::Map<std::string, const Service*>, available services
Returns:
- void;
*/
inline void displayAllServices(util::Map<std::string, const Service*> currentServices)
{
if (currentServices.getSize() == 0)
{
std::cout << "No Services Currently Available.\n";
return;
}
std::cout << std::left
<< std::setw(12) << "Service ID"
<< std::setw(35) << "Name"
<< std::setw(10) << "Labor Cost"
<< std::endl;
for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
{
const Service* currentService = currentServices.getValueAt(iterator);
if (currentService == nullptr || currentService->getState() == util::State::INACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(12) << currentService->getId()
<< std::setw(35) << util::truncateString(currentService->getName(), 30)
<< std::setw(10) << currentService->getLaborCost()
<< std::endl;
}
}
/* /*
Function: selectServicesToRemove Function: selectServicesToRemove
Description: Allows selection of a service to remove by index. Description: Allows selection of a service to remove by index.
@@ -49,7 +84,7 @@ inline std::string selectServicesToRemove(util::Map<std::string, const Service*>
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(6) << "Index"
<< std::setw(12) << "Service ID" << std::setw(12) << "Service ID"
<< std::setw(20) << "Name" << std::setw(35) << "Name"
<< std::setw(10) << "Labor Cost" << std::setw(10) << "Labor Cost"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < currentServices.getSize(); iterator++) for (int iterator = 0; iterator < currentServices.getSize(); iterator++)
@@ -62,7 +97,7 @@ inline std::string selectServicesToRemove(util::Map<std::string, const Service*>
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(6) << currentIndex
<< std::setw(12) << currentService->getId() << std::setw(12) << currentService->getId()
<< std::setw(20) << currentService->getName() << std::setw(35) << util::truncateString(currentService->getName(), 30)
<< std::setw(10) << currentService->getLaborCost() << std::setw(10) << currentService->getLaborCost()
<< std::endl; << std::endl;
currentServicesMap.insert(currentIndex++, currentService); currentServicesMap.insert(currentIndex++, currentService);
@@ -94,13 +129,15 @@ inline void selectInventoryItems(util::Map<std::string, const InventoryItem*>& c
bool doRun = true; bool doRun = true;
util::Map<int, const InventoryItem*> currentInventoryMap; util::Map<int, const InventoryItem*> currentInventoryMap;
int choice; int choice;
if (currentInventoryItems.getSize() == 0) if (currentInventoryItems.isEmpty())
{ {
std::cout << "No Items Present, Inventory empty.\n"; std::cout << "No Items Present, Inventory empty.\n";
return; return;
} }
while (doRun) while (doRun)
{ {
util::clear();
std::cout << "Create Service\n";
std::cout << "\nSelect Required Items\n"; std::cout << "\nSelect Required Items\n";
bool hasInventoryItems = false; bool hasInventoryItems = false;
int currentIndex = 1; int currentIndex = 1;
@@ -119,6 +156,19 @@ inline void selectInventoryItems(util::Map<std::string, const InventoryItem*>& c
{ {
continue; continue;
} }
bool alreadySelected = false;
for (int iteratorOne = 0; iteratorOne < selectedInventoryItems.getSize(); iteratorOne++)
{
if (selectedInventoryItems[iteratorOne] == currentInventoryItem->getId())
{
alreadySelected = true;
break;
}
}
if (alreadySelected)
{
continue;
}
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(6) << currentIndex
<< std::setw(12) << currentInventoryItem->getId() << std::setw(12) << currentInventoryItem->getId()
@@ -133,10 +183,9 @@ inline void selectInventoryItems(util::Map<std::string, const InventoryItem*>& c
{ {
break; break;
} }
std::cout << "Select the item (Index) or enter -1 to exit: "; std::cout << "Select the item (Index) or enter 0 to exit: ";
util::read(choice); util::read(choice);
if (choice == 0)
if (choice == -1)
{ {
doRun = false; doRun = false;
} }
@@ -144,6 +193,7 @@ inline void selectInventoryItems(util::Map<std::string, const InventoryItem*>& c
{ {
selectedInventoryItems.push_back(currentInventoryMap.getValueAt(currentInventoryMap.find(choice))->getId()); selectedInventoryItems.push_back(currentInventoryMap.getValueAt(currentInventoryMap.find(choice))->getId());
std::cout << "Item added successfully.\n" << std::endl; std::cout << "Item added successfully.\n" << std::endl;
util::pressEnter();
} }
else else
{ {
@@ -160,7 +210,7 @@ Parameters:
Returns: Returns:
- util::Map<std::string, const ServiceBooking*>: map containing only active (PENDING) service bookings - util::Map<std::string, const ServiceBooking*>: map containing only active (PENDING) service bookings
*/ */
inline util::Map<std::string, const ServiceBooking*> filterActiveServiceBookings(util::Map<std::string, const ServiceBooking*> currentBookings) inline util::Map<std::string, const ServiceBooking*> filterActiveServiceBookings(util::Map<std::string, const ServiceBooking*>& currentBookings)
{ {
util::Map<std::string, const ServiceBooking*> activeServiceBookings; util::Map<std::string, const ServiceBooking*> activeServiceBookings;
for (int iterator = 0; iterator < currentBookings.getSize(); iterator++) for (int iterator = 0; iterator < currentBookings.getSize(); iterator++)
@@ -201,8 +251,6 @@ inline bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& c
<< std::setw(15) << "VehicleNo" << std::setw(15) << "VehicleNo"
<< std::setw(15) << "Brand" << std::setw(15) << "Brand"
<< std::setw(15) << "Model" << std::setw(15) << "Model"
<< std::setw(20) << "Technician"
<< std::setw(15) << "TechnicianID"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < bookingsSize; iterator++) for (int iterator = 0; iterator < bookingsSize; iterator++)
{ {
@@ -219,8 +267,6 @@ inline bool listServiceBookings(util::Map<std::string, const ServiceBooking*>& c
<< std::setw(15) << currentBooking->getVehicleNumber() << std::setw(15) << currentBooking->getVehicleNumber()
<< std::setw(15) << currentBooking->getVehicleBrand() << std::setw(15) << currentBooking->getVehicleBrand()
<< std::setw(15) << currentBooking->getVehicleModel() << std::setw(15) << currentBooking->getVehicleModel()
<< std::setw(20) << ((currentAssignedTechnician == nullptr || currentAssignedTechnician->getName().empty()) ? "NULL" : currentAssignedTechnician->getName())
<< std::setw(15) << ((currentAssignedTechnician == nullptr || currentAssignedTechnician->getId().empty()) ? "NULL" : currentAssignedTechnician->getId())
<< std::endl; << std::endl;
serviceBookingsMap.insert(currentIndex++, currentBooking); serviceBookingsMap.insert(currentIndex++, currentBooking);
} }
@@ -239,7 +285,7 @@ Returns:
inline const ServiceBooking* selectPendingServiceBookings(util::Map<int, const ServiceBooking*>& serviceBookingsMap) inline const ServiceBooking* selectPendingServiceBookings(util::Map<int, const ServiceBooking*>& serviceBookingsMap)
{ {
int userInputIndex; int userInputIndex;
std::cout << "\nEnter a valid service index: "; std::cout << "\nEnter a service index: ";
util::read(userInputIndex); util::read(userInputIndex);
if (serviceBookingsMap.find(userInputIndex) != -1) if (serviceBookingsMap.find(userInputIndex) != -1)
{ {
@@ -304,7 +350,7 @@ Returns:
inline const User* selectTechnician(util::Map<int, const User*>& currentAvailableTechniciansMap) inline const User* selectTechnician(util::Map<int, const User*>& currentAvailableTechniciansMap)
{ {
int userInputIndex; int userInputIndex;
std::cout << "\nEnter valid technician index: "; std::cout << "\nEnter technician index: ";
util::read(userInputIndex); util::read(userInputIndex);
if (currentAvailableTechniciansMap.find(userInputIndex) != -1) if (currentAvailableTechniciansMap.find(userInputIndex) != -1)
{ {
@@ -327,19 +373,18 @@ Returns:
*/ */
inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string, const Invoice*>& currentInvoices) inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string, const Invoice*>& currentInvoices)
{ {
util::clear();
int currentIndex = 1, choice; int currentIndex = 1, choice;
util::Map<int, const Invoice*> pendingInvoicesForPayment; util::Map<int, const Invoice*> pendingInvoicesForPayment;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(8) << "Index"
<< std::setw(12) << "BookingID" << std::setw(15) << "Booking ID"
<< std::setw(15) << "VehicleBrand" << std::setw(20) << "Vehicle Brand"
<< std::setw(15) << "VehicleNumber" << std::setw(20) << "Vehicle Number"
<< std::setw(12) << "Technician ID" << std::setw(18) << "Technician ID"
<< std::setw(20) << "Technician Name" << std::setw(25) << "Technician Name"
<< std::setw(10) << "Discount(%)" << std::setw(15) << "Discount(%)"
<< std::setw(12) << "TotalAmount" << std::setw(15) << "TotalAmount"
<< std::setw(20) << "InvoiceDate" << std::setw(22) << "Invoice Timestamp"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++) for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++)
{ {
@@ -348,17 +393,17 @@ inline std::string selectInvoiceFromUserForPayment(const util::Map<std::string,
{ {
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician(); const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(8) << currentIndex
<< std::setw(12) << currentInvoice->getBookingId() << std::setw(15) << currentInvoice->getBookingId()
<< std::setw(15) << currentInvoice->getBooking()->getVehicleBrand() << std::setw(20) << currentInvoice->getBooking()->getVehicleBrand()
<< std::setw(15) << currentInvoice->getBooking()->getVehicleNumber() << std::setw(20) << currentInvoice->getBooking()->getVehicleNumber()
<< std::setw(12) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ? << std::setw(18) << ((currentTechnician != nullptr && currentTechnician->getId() != "") ?
currentTechnician->getId() : "Null") currentTechnician->getId() : "Null")
<< std::setw(20) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ? << std::setw(25) << ((currentTechnician != nullptr && currentTechnician->getName() != "") ?
currentTechnician->getName() : "Null") currentTechnician->getName() : "Null")
<< std::setw(10) << currentInvoice->getDiscountPercentage() << std::setw(15) << currentInvoice->getDiscountPercentage()
<< std::setw(12) << currentInvoice->getTotalAmount() << std::setw(15) << currentInvoice->getTotalAmount()
<< std::setw(20) << currentInvoice->getInvoiceDate().toString() << std::setw(22) << currentInvoice->getInvoiceDate().toString()
<< std::endl; << std::endl;
pendingInvoicesForPayment.insert(currentIndex++, currentInvoice); pendingInvoicesForPayment.insert(currentIndex++, currentInvoice);
} }
@@ -394,6 +439,9 @@ Returns:
inline util::PaymentMode selectPaymentMode() inline util::PaymentMode selectPaymentMode()
{ {
int choice; int choice;
while (true)
{
util::clear();
std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: "; std::cout << "Enter the payment Mode\n1.OFFLINE\n2.ONLINE\nChoice: ";
util::read(choice); util::read(choice);
if (choice == 1) if (choice == 1)
@@ -408,8 +456,76 @@ inline util::PaymentMode selectPaymentMode()
} }
else else
{ {
std::cout << "Invalid choice. Offline mode selected.\n"; std::cout << "Invalid choice. Try again.\n";
return util::PaymentMode::OFFLINE; util::pressEnter();
}
}
}
/*
Function: displayInvoicesInTabularForm
Description:
Displays all invoices in a tabular format. Each row shows booking details,
vehicle info, technician details, discount, total amount, invoice date,
and payment status. If inventory items exist for an invoice, they are
displayed in a separate table below the invoice row.
Parameters:
- currentInvoices: util::Map<std::string, const Invoice*>
Map of invoice IDs to Invoice pointers.
Returns:
- void
*/
inline const Invoice* selectInvoiceToDisplay(util::Map<std::string, const Invoice*>& currentInvoices)
{
int currentIndex = 1, choice;
util::Map<int, const Invoice*> currentInvoicesIndexMap;
if (currentInvoices.isEmpty())
{
std::cout << "No invoices available.\n\n";
return nullptr;
}
std::cout
<< std::left
<< std::setw(10) << "Index"
<< std::setw(12) << "BookingID"
<< std::setw(20) << "Vehicle Number"
<< std::setw(20) << "Technician Name"
<< std::setw(15) << "Total Amount"
<< std::setw(25) << "Invoice Date"
<< std::setw(20) << "Payment Status"
<< std::setw(15) << "Payment Mode"
<< std::endl;
for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++)
{
const Invoice* currentInvoice = currentInvoices.getValueAt(iterator);
if (!currentInvoice)
{
continue;
}
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician();
std::cout << std::left
<< std::setw(10) << currentIndex
<< std::setw(12) << currentInvoice->getBookingId()
<< std::setw(20) << currentInvoice->getBooking()->getVehicleNumber()
<< std::setw(20) << ((currentTechnician && !currentTechnician->getName().empty()) ? currentTechnician->getName() : "NULL")
<< std::setw(15) << currentInvoice->getTotalAmount()
<< std::setw(25) << currentInvoice->getInvoiceDate().toString()
<< std::setw(20) << util::getPaymentStatusString(currentInvoice->getStatus())
<< std::setw(15) << util::getPaymentModeString(currentInvoice->getPaymentMethod())
<< std::endl;
currentInvoicesIndexMap.insert(currentIndex++, currentInvoice);
}
std::cout << "Enter an index: ";
util::read(choice);
int currentSelectedIndex = currentInvoicesIndexMap.find(choice);
if (currentSelectedIndex != -1)
{
return currentInvoicesIndexMap.getValueAt(currentSelectedIndex);
}
else
{
std::cout << "Enter a valid index.\n";
return nullptr;
} }
} }
@@ -426,34 +542,48 @@ Throws:
*/ */
inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserInvoices) inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserInvoices)
{ {
std::cout << std::endl;
if (currentUserInvoices.getSize() == 0) if (currentUserInvoices.getSize() == 0)
{ {
std::cout << "No invoices found for this account." << std::endl << std::endl; std::cout << "No invoices found for this account.\n\n";
return; return;
} }
else else
{ {
for (int index = 0; index < currentUserInvoices.getSize(); index++) bool doRun = true;
do
{ {
const Invoice* currentInvoice = currentUserInvoices.getValueAt(index); const Invoice* selectedInvoice;
if (currentInvoice) int choice;
selectedInvoice = selectInvoiceToDisplay(currentUserInvoices);
if (selectedInvoice)
{ {
const User* currentTechnician = currentInvoice->getBooking()->getAssignedTechnician(); const User* currentTechnician = selectedInvoice->getBooking()->getAssignedTechnician();
std::cout << "\nInvoice Details\n"; util::clear();
std::cout << "Booking ID: " << currentInvoice->getBookingId() << std::endl; std::cout << "Invoice Details\n";
std::cout << "Vehicle Brand: " << currentInvoice->getBooking()->getVehicleBrand() << std::endl; std::cout << std::left << std::setw(20) << "Booking ID:"
std::cout << "Vehicle Number: " << currentInvoice->getBooking()->getVehicleNumber() << std::endl; << selectedInvoice->getBookingId() << std::endl;
std::cout << "Technician ID: " << std::cout << std::left << std::setw(20) << "Vehicle Brand:"
((currentTechnician != nullptr && currentTechnician->getId() != "") ? << selectedInvoice->getBooking()->getVehicleBrand() << std::endl;
currentTechnician->getId() : "Null") << std::endl; std::cout << std::left << std::setw(20) << "Vehicle Number:"
std::cout << "Technician Name: " << << selectedInvoice->getBooking()->getVehicleNumber() << std::endl;
((currentTechnician != nullptr && currentTechnician->getName() != "") ? std::cout << std::left << std::setw(20) << "Technician ID:"
currentTechnician->getName() : "Null") << std::endl; << ((currentTechnician != nullptr && !currentTechnician->getId().empty())
std::cout << "Discount(%): " << currentInvoice->getDiscountPercentage() << std::endl; ? currentTechnician->getId() : "NULL") << std::endl;
std::cout << "Total Amount: " << currentInvoice->getTotalAmount() << std::endl; std::cout << std::left << std::setw(20) << "Technician Name:"
std::cout << "Invoice Date: " << currentInvoice->getInvoiceDate().toString() << std::endl; << ((currentTechnician != nullptr && !currentTechnician->getName().empty())
std::cout << "Payment Status: " << util::getPaymentStatusString(currentInvoice->getStatus()) << std::endl; ? currentTechnician->getName() : "NULL") << std::endl;
auto inventoryItemsInInvoice = currentInvoice->getParts(); std::cout << std::left << std::setw(20) << "Discount(%):"
<< selectedInvoice->getDiscountPercentage() << std::endl;
std::cout << std::left << std::setw(20) << "Total Amount:"
<< selectedInvoice->getTotalAmount() << std::endl;
std::cout << std::left << std::setw(20) << "Invoice Date:"
<< selectedInvoice->getInvoiceDate().toString() << std::endl;
std::cout << std::left << std::setw(20) << "Payment Status:"
<< util::getPaymentStatusString(selectedInvoice->getStatus()) << std::endl;
std::cout << std::left << std::setw(20) << "Payment Mode:"
<< util::getPaymentModeString(selectedInvoice->getPaymentMethod()) << std::endl;
auto inventoryItemsInInvoice = selectedInvoice->getParts();
if (inventoryItemsInInvoice.isEmpty()) if (inventoryItemsInInvoice.isEmpty())
{ {
std::cout << "No inventory items used.\n\n"; std::cout << "No inventory items used.\n\n";
@@ -475,14 +605,89 @@ inline void displayInvoices(util::Map<std::string, const Invoice*> currentUserIn
<< std::setw(10) << currentItem->getPrice() << std::setw(10) << currentItem->getPrice()
<< std::endl; << std::endl;
} }
std::cout << "\n\nDo you want to display another Invoice (1-Yes, 2-No): ";
util::read(choice);
if (choice == 1)
{
doRun = true;
util::clear();
}
else if (choice == 2)
{
doRun = false;
} }
else else
{ {
throw std::runtime_error("Null invoice encountered while displaying invoices."); std::cout << "Invalid choice\n";
doRun = false;
} }
} }
else
{
std::cout << "Unable to fetch the selected invoice\n";
doRun = false;
}
} while (doRun);
}
}
/*
Function: filterStartedJobCards
Description: Filters the given list of job cards and returns only those with status STARTED.
Parameters:
- assignedJobCards: Map of job card IDs to JobCard pointers.
Returns:
- util::Map<std::string, const JobCard*> containing only job cards with status STARTED.
*/
inline util::Map<std::string, const JobCard*> filterStartedJobCards(util::Map<std::string, const JobCard*>& assignedJobCards)
{
util::Map<std::string, const JobCard*> startedJobCards;
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && currentJobCard->getStatus() == util::ServiceJobStatus::STARTED)
{
startedJobCards.insert(currentJobCard->getId(), currentJobCard);
}
}
return startedJobCards;
}
/*
Function: displayAllJobs
Description: Displays all Jobs assigned to a Technician
Parameters:
- assignedJobCards: util::Map<std::string, const JobCard*>&, job cards assigned to the technician
Returns:
- std::string: ID of the selected job card, or empty string if none selected
*/
inline void displayAllJobs(util::Map<std::string, const JobCard*>& assignedJobCards)
{
if (assignedJobCards.getSize() == 0)
{
std::cout << "No active jobs assigned.\n";
return;
}
std::cout << std::endl;
std::cout << std::left
<< 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(12) << currentJobCard->getBookingId()
<< std::setw(12) << currentJobCard->getId()
<< std::setw(20) << currentJobCard->getService()->getName()
<< std::setw(12) << currentJobCard->getServiceId()
<< std::endl;
}
} }
util::pressEnter();
} }
/* /*
@@ -490,15 +695,20 @@ Function: selectJobCardToComplete
Description: Lists all incomplete job cards assigned to the technician and allows selection by index. Description: Lists all incomplete job cards assigned to the technician and allows selection by index.
Parameters: Parameters:
- assignedJobCards: util::Map<std::string, const JobCard*>&, job cards assigned to the technician - assignedJobCards: util::Map<std::string, const JobCard*>&, job cards assigned to the technician
- incompleteJobCards: util::Map<int, const JobCard*>&, map of incomplete job cards indexed for selection
Returns: Returns:
- std::string: ID of the selected job card, or empty string if none selected - std::string: ID of the selected job card, or empty string if none selected
*/ */
inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*>& assignedJobCards, util::Map<int, const JobCard*>& incompleteJobCards) inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*>& assignedJobCards)
{
util::Map<int, const JobCard* > incompleteJobCards;
if (assignedJobCards.getSize() == 0)
{ {
std::cout << "No started jobs available to complete.\n";
return "";
}
int currentIndex = 1; int currentIndex = 1;
int choice; int choice;
bool hasIncompleteJobCard = false; std::cout << std::endl;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(6) << "Index"
<< std::setw(12) << "BookingID" << std::setw(12) << "BookingID"
@@ -517,14 +727,8 @@ inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
<< std::setw(20) << currentJobCard->getService()->getName() << std::setw(20) << currentJobCard->getService()->getName()
<< std::setw(12) << currentJobCard->getServiceId() << std::setw(12) << currentJobCard->getServiceId()
<< std::endl; << std::endl;
hasIncompleteJobCard = true;
incompleteJobCards.insert(currentIndex++, currentJobCard); incompleteJobCards.insert(currentIndex++, currentJobCard);
} }
}
if (!hasIncompleteJobCard)
{
std::cout << "No pending jobs are present.\n";
return "";
} }
std::cout << "Select the Job Card to complete (Index): "; std::cout << "Select the Job Card to complete (Index): ";
util::read(choice); util::read(choice);
@@ -537,6 +741,7 @@ inline std::string selectJobCardToComplete(util::Map<std::string, const JobCard*
else else
{ {
std::cout << "Invalid index.\n"; std::cout << "Invalid index.\n";
std::cout << "Failed to complete jobs.\n\n";
return ""; return "";
} }
} }
@@ -553,9 +758,9 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
{ {
util::Map<int, const Notification*> indexedNotifications; util::Map<int, const Notification*> indexedNotifications;
std::cout << std::left std::cout << std::left
<< std::setw(6) << "Index" << std::setw(10) << "Index"
<< std::setw(15) << "ID" << std::setw(15) << "ID"
<< std::setw(30) << "Title" << std::setw(35) << "Title"
<< std::setw(25) << "Timestamp" << std::setw(25) << "Timestamp"
<< std::endl; << std::endl;
int currentIndex = 1; int currentIndex = 1;
@@ -565,9 +770,9 @@ inline const Notification* selectNotification(const util::Vector<const Notificat
if (currentNotification) if (currentNotification)
{ {
std::cout << std::left std::cout << std::left
<< std::setw(6) << currentIndex << std::setw(10) << currentIndex
<< std::setw(15) << currentNotification->getId() << std::setw(15) << currentNotification->getId()
<< std::setw(35) << currentNotification->getTitle() << std::setw(35) << util::truncateString(currentNotification->getTitle(), 30)
<< std::setw(25) << currentNotification->getCreatedAt().toString() << std::setw(25) << currentNotification->getCreatedAt().toString()
<< std::endl; << std::endl;
indexedNotifications.insert(currentIndex, currentNotification); indexedNotifications.insert(currentIndex, currentNotification);
@@ -709,17 +914,18 @@ inline util::Map<std::string, const User*> filterActiveUsers(const util::Map<std
} }
/* /*
Function: displayAllActiveUsers Function: displayAllUsers
Description: Displays all active users in a tabular format with index, ID, username, and type. Description: Displays all active users in a tabular format with index, ID, username, and type.
Parameter: util::Map<std::string, const User*>& activeUsers - active users list Parameter: util::Map<std::string, const User*>& activeUsers - active users list
int activeUserCount - number of active users
Return type: void Return type: void
*/ */
inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUsers, int activeUserCount) inline void displayAllUsers(util::Map<std::string, const User*>& activeUsers)
{ {
int activeUserCount = activeUsers.getSize();
std::cout << std::left << std::setw(10) << "Index" std::cout << std::left << std::setw(10) << "Index"
<< std::setw(15) << "User ID" << std::setw(15) << "User ID"
<< std::setw(25) << "Username" << std::setw(25) << "Username"
<< std::setw(25) << "Full Name"
<< std::setw(25) << "User Type" << std::setw(25) << "User Type"
<< std::endl; << std::endl;
for (int iterator = 0; iterator < activeUserCount; iterator++) for (int iterator = 0; iterator < activeUserCount; iterator++)
@@ -730,6 +936,7 @@ inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUse
std::cout << std::left << std::setw(10) << (iterator + 1) std::cout << std::left << std::setw(10) << (iterator + 1)
<< std::setw(15) << user->getId() << std::setw(15) << user->getId()
<< std::setw(25) << user->getUserName() << std::setw(25) << user->getUserName()
<< std::setw(25) << user->getName()
<< std::setw(25) << util::getUserTypeString(user->getUserType()) << std::setw(25) << util::getUserTypeString(user->getUserType())
<< std::endl; << std::endl;
} }
@@ -750,7 +957,7 @@ Parameters:
Returns: Returns:
- util::Map<std::string, const Service*> containing only active services. - util::Map<std::string, const Service*> containing only active services.
*/ */
inline util::Map<std::string, const Service*> filterActiveServices(util::Map<std::string, const Service*> serviceList) inline util::Map<std::string, const Service*> filterActiveServices(util::Map<std::string, const Service*>& serviceList)
{ {
util::Map<std::string, const Service*> activeServices; util::Map<std::string, const Service*> activeServices;
for (int iterator = 0; iterator < serviceList.getSize(); iterator++) for (int iterator = 0; iterator < serviceList.getSize(); iterator++)
@@ -777,10 +984,10 @@ inline const Service* selectServiceFromServices(const util::Map<std::string, con
std::cout << "No active services available." << std::endl; std::cout << "No active services available." << std::endl;
return nullptr; return nullptr;
} }
std::cout << std::endl;
util::Map<int, const Service*> activeServicesMap; util::Map<int, const Service*> activeServicesMap;
int currentIndex = 1; int currentIndex = 1;
int userInputIndex; int userInputIndex;
std::cout << std::endl;
std::cout << std::left std::cout << std::left
<< std::setw(10) << "Index" << std::setw(10) << "Index"
<< std::setw(15) << "Service ID" << std::setw(15) << "Service ID"
@@ -824,6 +1031,68 @@ inline const Service* selectServiceFromServices(const util::Map<std::string, con
return activeServicesMap[userInputIndex]; return activeServicesMap[userInputIndex];
} }
/*
Function: filterComboPackages
Description:
Filters the given list of combo packages and returns only those that are ACTIVE.
Parameters:
- comboPackages: util::Map<std::string, const ComboPackage*>&
Map of combo package IDs to ComboPackage pointers.
Returns:
- util::Map<std::string, const ComboPackage*>
Map containing only active combo packages.
*/
inline util::Map<std::string, const ComboPackage*> filterComboPackages(util::Map<std::string, const ComboPackage*>& comboPackages)
{
util::Map<std::string, const ComboPackage*> activeComboPackages;
for (int iterator = 0; iterator < comboPackages.getSize(); iterator++)
{
const ComboPackage* currentComboPackage = comboPackages.getValueAt(iterator);
if (currentComboPackage && currentComboPackage->getState() == util::State::ACTIVE)
{
activeComboPackages.insert(currentComboPackage->getId(), currentComboPackage);
}
}
return activeComboPackages;
}
/*
Function: displayAllComboPackages
Description: Displays all active combo packages
Parameters:
- currentComboPackages: util::Map<std::string, const ComboPackage*>, available combo packages
Returns:
- void;
*/
inline void displayAllComboPackages(util::Map<std::string, const ComboPackage*> comboPackages)
{
std::cout << std::endl;
if (comboPackages.getSize() == 0)
{
std::cout << "No active combo packages available." << std::endl;
return;
}
std::cout << std::left
<< std::setw(15) << "Combo ID"
<< std::setw(35) << "Combo Name"
<< std::setw(15) << "Estimate Cost"
<< std::endl;
for (int index = 0; index < comboPackages.getSize(); index++)
{
const ComboPackage* currentComboPackage = comboPackages.getValueAt(index);
if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE)
{
continue;
}
std::cout << std::left
<< std::setw(15) << currentComboPackage->getId()
<< std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage)
<< std::endl;
}
}
/* /*
Function: selectComboPackageFromPackages Function: selectComboPackageFromPackages
Description: Displays active combo packages and allows the customer to select one by index. Description: Displays active combo packages and allows the customer to select one by index.
@@ -835,16 +1104,17 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::Map<std::s
util::Map<int, const ComboPackage*> activeComboPackages; util::Map<int, const ComboPackage*> activeComboPackages;
int currentIndex = 1; int currentIndex = 1;
int userInputIndex; int userInputIndex;
std::cout << std::endl;
std::cout << std::left std::cout << std::left
<< std::setw(10) << "Index" << std::setw(10) << "Index"
<< std::setw(15) << "Combo ID" << std::setw(15) << "Combo ID"
<< std::setw(15) << "Combo Name" << std::setw(35) << "Combo Name"
<< std::setw(15) << "Estimate Cost" << std::setw(15) << "Estimate Cost"
<< std::endl; << std::endl;
for (int index = 0; index < comboPackages.getSize(); index++) for (int index = 0; index < comboPackages.getSize(); index++)
{ {
const ComboPackage* currentComboPackage = comboPackages.getValueAt(index); const ComboPackage* currentComboPackage = comboPackages.getValueAt(index);
if (currentComboPackage->getState() != util::State::ACTIVE) if (currentComboPackage && currentComboPackage->getState() != util::State::ACTIVE)
{ {
continue; continue;
} }
@@ -852,7 +1122,7 @@ inline const ComboPackage* selectComboPackageFromPackages(const util::Map<std::s
std::cout << std::left std::cout << std::left
<< std::setw(10) << currentIndex << std::setw(10) << currentIndex
<< std::setw(15) << currentComboPackage->getId() << std::setw(15) << currentComboPackage->getId()
<< std::setw(25) << currentComboPackage->getPackageName() << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage) << std::setw(15) << util::calculateComboServiceEstimatedCost(currentComboPackage)
<< std::endl; << std::endl;
currentIndex++; currentIndex++;
@@ -886,7 +1156,7 @@ inline bool getNotificationPreference(const std::string& serviceName)
while (true) while (true)
{ {
util::clear(); util::clear();
std::cout << " Configure Notification Preferences\n"; std::cout << "Configure Notification Preferences\n";
std::cout << "\n" << serviceName << " Notifications\n"; std::cout << "\n" << serviceName << " Notifications\n";
std::cout << "1. Enable Notifications\n"; std::cout << "1. Enable Notifications\n";
std::cout << "2. Disable Notifications\n"; std::cout << "2. Disable Notifications\n";
@@ -920,7 +1190,7 @@ inline util::Map<std::string, const InventoryItem*> filterActiveItems(const util
for (int index = 0; index < inventorySize; index++) for (int index = 0; index < inventorySize; index++)
{ {
const InventoryItem* item = inventoryItems.getValueAt(index); const InventoryItem* item = inventoryItems.getValueAt(index);
if (item != nullptr && item->getState() != util::State::INACTIVE) if (item && item->getState() != util::State::INACTIVE)
{ {
activeItems.insert(item->getId(), item); activeItems.insert(item->getId(), item);
} }
@@ -1025,21 +1295,21 @@ inline void displayComboPackagesWithIndex(util::Map<int, const ComboPackage*>& c
const ComboPackage* currentComboPackage = currentComboPackageIndexMap.getValueAt(iterator); const ComboPackage* currentComboPackage = currentComboPackageIndexMap.getValueAt(iterator);
if (currentComboPackage == nullptr) if (currentComboPackage == nullptr)
{ {
throw std::runtime_error("Error accessing the combopackage.\n"); throw std::runtime_error("Error accessing the combo package.\n");
} }
if (iterator == 0) if (iterator == 0)
{ {
std::cout << std::left std::cout << std::left
<< std::setw(8) << "Index" << std::setw(8) << "Index"
<< std::setw(10) << "ID" << std::setw(10) << "ID"
<< std::setw(20) << "Package Name" << std::setw(35) << "Package Name"
<< std::setw(15) << "Discount (%)" << std::setw(15) << "Discount (%)"
<< "\n"; << "\n";
} }
std::cout << std::left std::cout << std::left
<< std::setw(8) << currentComboPackageIndexMap.getKeyAt(iterator) << std::setw(8) << currentComboPackageIndexMap.getKeyAt(iterator)
<< std::setw(10) << currentComboPackage->getId() << std::setw(10) << currentComboPackage->getId()
<< std::setw(20) << currentComboPackage->getPackageName() << std::setw(35) << util::truncateString(currentComboPackage->getPackageName(), 30)
<< std::setw(15) << currentComboPackage->getDiscountPercentage() << std::setw(15) << currentComboPackage->getDiscountPercentage()
<< "\n"; << "\n";
} }
@@ -34,10 +34,11 @@ void TechnicianMenu::showMenu()
int choice; int choice;
util::clear(); util::clear();
std::cout << "Technician Menu" std::cout << "Technician Menu"
<< "\n1. Mark Job as Completed" << "\n1. Display My Jobs"
<< "\n2. View Notifications" << "\n2. Mark Job as Completed"
<< "\n3. Change Password" << "\n3. View Notifications"
<< "\n4. Logout" << "\n4. Change Password"
<< "\n5. Logout"
<< "\nEnter a choice: "; << "\nEnter a choice: ";
util::read(choice); util::read(choice);
if (!handleOperation(choice)) if (!handleOperation(choice))
@@ -64,15 +65,18 @@ bool TechnicianMenu::handleOperation(int choice)
switch (choice) switch (choice)
{ {
case 1: case 1:
completeJob(); displayJobs();
break; break;
case 2: case 2:
viewNotifications(); completeJob();
break; break;
case 3: case 3:
changePassword(); viewNotifications();
break; break;
case 4: case 4:
changePassword();
break;
case 5:
logout(); logout();
return false; return false;
default: default:
@@ -82,6 +86,24 @@ bool TechnicianMenu::handleOperation(int choice)
return true; return true;
} }
/*
Function: displayJobs
Description: Displays all Jobs assigned to a Technician
Parameters:
- None
Returns:
- void
*/
void TechnicianMenu::displayJobs()
{
util::clear();
std::cout << "My Jobs\n";
util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser();
util::Map<std::string, const JobCard*> startedJobCards = filterStartedJobCards(assignedJobCards);
displayAllJobs(startedJobCards);
util::pressEnter();
}
/* /*
Function: completeJob Function: completeJob
Description: Allows the technician to mark a selected job card as completed. Description: Allows the technician to mark a selected job card as completed.
@@ -93,18 +115,15 @@ Returns:
*/ */
void TechnicianMenu::completeJob() void TechnicianMenu::completeJob()
{ {
util::clear();
std::cout << "Complete Job\n";
util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser(); util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser();
util::Map<int, const JobCard*> incompleteJobCards; util::Map<std::string, const JobCard*> startedJobCards = filterStartedJobCards(assignedJobCards);
std::cout << "Jobs to be completed.\n"; std::string selectedJobID = selectJobCardToComplete(startedJobCards);
std::string selectedJobID = selectJobCardToComplete(assignedJobCards, incompleteJobCards); if (!selectedJobID.empty())
if (selectedJobID == "")
{
std::cout << "Failed to complete the job.\n";
}
else
{ {
m_controller.completeJob(selectedJobID); m_controller.completeJob(selectedJobID);
std::cout << "Job marked as completed.\n"; std::cout << "\nJob marked as completed.\n\n";
} }
util::pressEnter(); util::pressEnter();
} }
@@ -17,6 +17,7 @@ private:
bool handleOperation(int choice); bool handleOperation(int choice);
public: public:
void showMenu(); void showMenu();
void displayJobs();
void completeJob(); void completeJob();
void viewNotifications(); void viewNotifications();
void logout(); void logout();
@@ -7,6 +7,8 @@ Author: Trenser
Date:19-May-2026 Date:19-May-2026
*/ */
#include <iostream>
#include <stdexcept>
#include "Enums.h" #include "Enums.h"
#include "InputHelper.h" #include "InputHelper.h"
#include "OutputHelper.h" #include "OutputHelper.h"
@@ -23,6 +25,8 @@ Return type: void
*/ */
void UserInterface::run() void UserInterface::run()
{ {
try
{
m_controller.loadSystemData(); m_controller.loadSystemData();
m_controller.runSystemChecks(); m_controller.runSystemChecks();
bool isMenuActive = true; bool isMenuActive = true;
@@ -46,6 +50,19 @@ void UserInterface::run()
} }
} }
m_controller.saveSystemData(); m_controller.saveSystemData();
}
catch (const std::invalid_argument& exception)
{
std::cout << "Exception: Invalid Argument: " << exception.what() << std::endl;
}
catch (const std::exception& exception)
{
std::cout << "Exception: " << exception.what() << std::endl;
}
catch (...)
{
std::cout << "Unknown error occurred." << std::endl;
}
} }
/* /*