Compare commits

..

13 Commits

Author SHA1 Message Date
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
10 changed files with 444 additions and 187 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);
} }
@@ -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 (type == util::UserType::CUSTOMER)
{
booking->setStatus(util::ServiceJobStatus::CANCELLED);
booking->setCustomer(nullptr);
booking->setCustomerId("");
User* assignedTechnician = booking->getAssignedTechnician();
std::string title = "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.";
sendNotification(assignedTechnician, title, message);
}
else if (type == util::UserType::TECHNICIAN)
{
booking->setStatus(util::ServiceJobStatus::PENDING);
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.";
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);
}
}
}
}
}
} }
if (booking->getCustomerId() != customerID)
{
continue;
}
if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
{
continue;
}
User* assignedTechnician = booking->getAssignedTechnician();
std::string titleToTechnician = "Customer Service Cancelled";
std::string messageToTechnician = "The customer has cancelled their service booking. Your assigned job card has been cancelled and the inventory has been restocked.";
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.";
processBookingCancellation(booking,
util::ServiceJobStatus::CANCELLED,
titleToTechnician, messageToTechnician, assignedTechnician,
util::ServiceJobStatus::CANCELLED,
jobTitle, jobMessage, jobs, *this
);
} }
} }
@@ -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();
auto& jobs = m_dataStore.getJobCards(); int userIndex = users.find(technicianID);
for (int jobIterator = 0; jobIterator < jobs.getSize(); jobIterator++) if (userIndex == -1)
{ {
JobCard* job = jobs.getValueAt(jobIterator); throw std::runtime_error("User not found: " + technicianID);
if (job != nullptr && job->getTechnicianId() == 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();
for (int iteratorOne = 0; iteratorOne < bookings.getSize(); iteratorOne++)
{
ServiceBooking* booking = bookings.getValueAt(iteratorOne);
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);
} }
} }
@@ -306,6 +306,14 @@ void UserManagementService::removeUser(const std::string& userID)
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); inventoryManagementService.detach(user);
paymentManagementService.detach(user); paymentManagementService.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");
} }
@@ -263,6 +263,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();
@@ -402,6 +403,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 +434,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
{ {
@@ -541,7 +547,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();
@@ -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();
} }
@@ -49,7 +49,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 +62,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 +94,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 +121,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 +148,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 +158,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 +175,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 +216,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 +232,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 +250,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 +315,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)
{ {
@@ -416,6 +427,73 @@ inline util::PaymentMode selectPaymentMode()
} }
} }
/*
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;
}
}
/* /*
Function: displayInvoices Function: displayInvoices
Description: Displays detailed information for all invoices associated with the customer, Description: Displays detailed information for all invoices associated with the customer,
@@ -429,34 +507,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";
@@ -478,14 +570,53 @@ 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
{
std::cout << "Invalid choice\n";
doRun = false;
}
} }
else else
{ {
throw std::runtime_error("Null invoice encountered while displaying invoices."); 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);
} }
} }
util::pressEnter(); return startedJobCards;
} }
/* /*
@@ -497,49 +628,50 @@ Parameters:
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)
{ {
int currentIndex = 1; std::cout << "No started jobs available to complete.\n";
int choice; return "";
bool hasIncompleteJobCard = false; }
std::cout << std::left int currentIndex = 1;
<< std::setw(6) << "Index" int choice;
<< std::setw(12) << "BookingID" std::cout << std::endl;
<< std::setw(12) << "JobID" std::cout << std::left
<< std::setw(20) << "ServiceName" << std::setw(6) << "Index"
<< std::setw(12) << "ServiceID" << std::setw(12) << "BookingID"
<< std::endl; << std::setw(12) << "JobID"
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++) << std::setw(20) << "ServiceName"
<< std::setw(12) << "ServiceID"
<< std::endl;
for (int iterator = 0; iterator < assignedJobCards.getSize(); iterator++)
{ {
const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator); const JobCard* currentJobCard = assignedJobCards.getValueAt(iterator);
if (currentJobCard && (currentJobCard->getStatus() == util::ServiceJobStatus::STARTED)) if (currentJobCard && (currentJobCard->getStatus() == util::ServiceJobStatus::STARTED))
{ {
std::cout << std::left << std::setw(6) << currentIndex std::cout << std::left << std::setw(6) << currentIndex
<< std::setw(12) << currentJobCard->getBookingId() << std::setw(12) << currentJobCard->getBookingId()
<< std::setw(12) << currentJobCard->getId() << std::setw(12) << currentJobCard->getId()
<< 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 << "Select the Job Card to complete (Index): ";
{
std::cout << "No pending jobs are present.\n";
return "";
}
std::cout << "Select the Job Card to complete (Index): ";
util::read(choice); util::read(choice);
int selectedJobCardIndex = incompleteJobCards.find(choice); int selectedJobCardIndex = incompleteJobCards.find(choice);
if (selectedJobCardIndex != -1) if (selectedJobCardIndex != -1)
{ {
const JobCard* selectedJobCard = incompleteJobCards.getValueAt(selectedJobCardIndex); const JobCard* selectedJobCard = incompleteJobCards.getValueAt(selectedJobCardIndex);
return selectedJobCard->getId(); return selectedJobCard->getId();
} }
else else
{ {
std::cout << "Invalid index.\n"; std::cout << "Invalid index.\n";
std::cout << "Failed to complete jobs.\n\n";
return ""; return "";
} }
} }
@@ -723,6 +855,7 @@ inline void displayAllActiveUsers(util::Map<std::string, const User*>& activeUse
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++)
@@ -733,6 +866,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;
} }
@@ -753,7 +887,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++)
@@ -780,6 +914,7 @@ 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;
@@ -826,6 +961,31 @@ 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: 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.
@@ -837,10 +997,11 @@ 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++)
@@ -854,7 +1015,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++;
@@ -922,7 +1083,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);
} }
@@ -1027,21 +1188,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";
} }
@@ -93,18 +93,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();
} }