Compare commits

..

2 Commits

Author SHA1 Message Date
Avinash Rajesh 07e21f2f60 Fixed Reviewed Changes 2026-06-01 16:49:57 +05:30
Avinash Rajesh aee6356e6f Implement Confirm Payment Functionality
<User Story> Complete Payments - 1797</User Story>

<Changes>
1. Added Controller::getAllInvoices
   - Retrieves all invoices from PaymentManagementService and returns them as a read-only map.
2. Implemented Controller::confirmPayment
   - Delegates payment confirmation for a given invoice ID to PaymentManagementService.
3. Introduced PaymentManagementService::getAllInvoice
   - Provides access to all invoices stored in the datastore.
4. Added PaymentManagementService::confirmPayment
   - Confirms payment for a specific invoice, updates payment date and status, and sends notification.
5. Extended util::PaymentStatus enum
   - Added PAID status and updated string conversion.
6. Integrated AdminMenu::confirmPayment
   - Validates invoice list, filters by status, allows selection, and confirms payment.
7. Updated CustomerMenu::completePayments
   - Uses parameterized status filtering for invoice selection.
8. Enhanced MenuHelper::selectInvoiceFromUserForPayment
   - Accepts requiredStatus parameter for flexible filtering.
9. Adjusted AdminMenu options
   - Added "Confirm Payment" before Logout.
</Changes>

<Test>
Acceptance Criteria:
1. Admin selects "Confirm Payment" from menu.
   - Verify system prompts and displays invoices filtered by status.
2. Admin selects invoice with status = PAID.
   - Verify payment confirmation updates date, sets status, and sends notification.
3. Admin attempts confirmation with empty invoice list.
   - Verify error message: "No pending invoices available for confirmation."
4. Customer completes payment.
   - Verify selection uses util::PaymentStatus::PENDING and payment flow works correctly.
5. Invalid invoice ID entered.
   - Verify system throws runtime_error with "Payment failed: invalid invoice ID."

Precondition:
1. Admin logged into system.
2. At least one invoice exists in datastore.
3. Notification system available.

Steps:
1. Navigate to Admin menu → Confirm Payment.
2. Select invoice with PAID status.
3. Confirm payment and check notification.
4. Attempt with empty invoice list.
5. Attempt with invalid invoice ID.
</Test>

<Review>
Sreeja Reghukumar
</Review>
2026-06-01 16:49:57 +05:30
4 changed files with 58 additions and 58 deletions
@@ -543,65 +543,50 @@ static void restoreInventory(ServiceBooking* booking)
/* /*
Function: processBookingCancellation Function: processBookingCancellation
Description: Handles cancellation or reassignment of a service booking based on user type. Description: Cancels jobs and updates the status of a given booking. Sends notifications to the
Cancels associated job cards, updates booking status, clears technician assignments, specified user, resets technician assignment if needed, and restores inventory items.
restores inventory, and sends appropriate notifications. Parameter: ServiceBooking* booking - Pointer to the booking being cancelled
Parameters: util::ServiceJobStatus newServiceBookingStatus - New status to assign to the booking
ServiceBooking* booking - The booking to cancel or reset const std::string& notificationTitle - Title of the booking cancellation notification
util::Map<std::string, JobCard*>& jobs - Collection of job cards to update const std::string& notificationMessage - Message body of the booking cancellation notification
ServiceManagementService& currentService - Service layer for notifications User* notifyUser - User to notify about the cancellation
util::UserType userType - Type of user initiating cancellation (CUSTOMER or TECHNICIAN) 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 Return type: void
*/ */
static void processBookingCancellation(ServiceBooking* booking, static void processBookingCancellation(ServiceBooking* booking,
util::Map<std::string, JobCard*>& jobs, util::ServiceJobStatus newServiceBookingStatus,
ServiceManagementService& currentService, const std::string& notificationTitle,
util::UserType userType) 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) if (!booking || !notifyUser)
{ {
return; return;
} }
for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator) for (int jobIterator = 0; jobIterator < jobs.getSize(); ++jobIterator)
{ {
JobCard* jobCard = jobs.getValueAt(jobIterator); JobCard* jobCard = jobs.getValueAt(jobIterator);
if (!jobCard || jobCard->getBookingId() != booking->getId() || jobCard->getStatus() == util::ServiceJobStatus::CANCELLED) if (jobCard && jobCard->getBookingId() == booking->getId())
{ {
continue; jobCard->setStatus(jobCardStatus);
} currentService.sendNotification(notifyUser, jobNotificationTitle, jobNotificationMessage);
jobCard->setStatus(util::ServiceJobStatus::CANCELLED);
if (userType == util::UserType::CUSTOMER)
{
if (User* technician = booking->getAssignedTechnician())
{
const std::string jobTitle = "Job Cancelled";
const std::string jobMessage = "Your job card has been cancelled and the inventory has been restocked.";
currentService.sendNotification(technician, jobTitle, jobMessage);
}
} }
} }
if (userType == util::UserType::CUSTOMER) booking->setStatus(newServiceBookingStatus);
currentService.sendNotification(notifyUser, notificationTitle, notificationMessage);
if (newServiceBookingStatus == util::ServiceJobStatus::PENDING)
{ {
booking->setStatus(util::ServiceJobStatus::CANCELLED); booking->setAssignedTechnician(nullptr);
if (User* technician = booking->getAssignedTechnician()) booking->setAssignedTechnicianId("");
{
const std::string title = "Customer Service Cancelled";
const std::string message = "Your assigned job card has been cancelled and the inventory has been restocked.";
currentService.sendNotification(technician, title, message);
}
} }
else if (userType == util::UserType::TECHNICIAN)
{
booking->setStatus(util::ServiceJobStatus::PENDING);
if (User* customer = booking->getCustomer())
{
const std::string title = "Technician Unavailable";
const std::string message = "Your booking has been reset to pending and we will reassign a new technician shortly.";
currentService.sendNotification(customer, title, message);
}
}
booking->setAssignedTechnician(nullptr);
booking->setAssignedTechnicianId("");
restoreInventory(booking); restoreInventory(booking);
} }
@@ -639,13 +624,21 @@ void ServiceManagementService::cancelCustomerServiceBookings(const std::string&
{ {
continue; continue;
} }
if (booking->getStatus() != util::ServiceJobStatus::PENDING && if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
booking->getStatus() != util::ServiceJobStatus::STARTED &&
booking->getStatus() != util::ServiceJobStatus::IN_PROGRESS)
{ {
continue; continue;
} }
processBookingCancellation(booking, jobs, *this, util::UserType::CUSTOMER); 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
);
} }
} }
@@ -683,9 +676,7 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia
{ {
continue; continue;
} }
if (booking->getStatus() != util::ServiceJobStatus::PENDING && if (booking->getStatus() != util::ServiceJobStatus::PENDING && booking->getStatus() != util::ServiceJobStatus::STARTED)
booking->getStatus() != util::ServiceJobStatus::STARTED &&
booking->getStatus() != util::ServiceJobStatus::IN_PROGRESS)
{ {
continue; continue;
} }
@@ -694,7 +685,14 @@ void ServiceManagementService::cancelTechnicianJobs(const std::string& technicia
{ {
continue; continue;
} }
processBookingCancellation(booking, jobs, *this, util::UserType::TECHNICIAN); 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
);
} }
} }
@@ -116,13 +116,15 @@ bool util::isPasswordValid(const std::string& password)
* usersMap - map of user objects keyed by identifier * usersMap - map of user objects keyed by identifier
* Returns: * Returns:
* bool - true if the username is already in use by an active user, false otherwise * bool - true if the username is already in use by an active user, false otherwise
* Notes:
* - Only considers users with state util::State::ACTIVE
*/ */
bool util::isUsernameDuplicate(const std::string& username, const util::Map<std::string, User*>& usersMap) bool util::isUsernameDuplicate(const std::string& username, const util::Map<std::string, User*>& usersMap)
{ {
int index = usersMap.findIf( int index = usersMap.findIf(
[&](const std::string&, User* user) [&](const std::string&, User* user)
{ {
return (user->getUserName() == username); return (user->getUserName() == username && user->getState() == util::State::ACTIVE);
} }
); );
return index != -1; return index != -1;
@@ -736,7 +736,7 @@ inline std::string selectJobCardToUpdate(util::Map<std::string, const JobCard*>&
util::Map<int, const JobCard* > incompleteJobCards; util::Map<int, const JobCard* > incompleteJobCards;
if (assignedJobCards.getSize() == 0) if (assignedJobCards.getSize() == 0)
{ {
std::cout << "\nNo jobs available.\n\n"; std::cout << "No jobs available.\n\n";
return ""; return "";
} }
int currentIndex = 1; int currentIndex = 1;
@@ -744,12 +744,12 @@ inline std::string selectJobCardToUpdate(util::Map<std::string, const JobCard*>&
if (selectedJobStatusType == util::ServiceJobStatus::STARTED) if (selectedJobStatusType == util::ServiceJobStatus::STARTED)
{ {
util::clear(); util::clear();
std::cout << "Select a job to mark as In Progress\n"; std::cout << "Select a job to update to Inprogress\n";
} }
else if (selectedJobStatusType == util::ServiceJobStatus::IN_PROGRESS) else if (selectedJobStatusType == util::ServiceJobStatus::IN_PROGRESS)
{ {
util::clear(); util::clear();
std::cout << "Select a job to mark as Completed\n"; std::cout << "Select a job to update to Completed\n";
} }
else else
{ {
@@ -780,7 +780,7 @@ inline std::string selectJobCardToUpdate(util::Map<std::string, const JobCard*>&
incompleteJobCards.insert(currentIndex++, currentJobCard); incompleteJobCards.insert(currentIndex++, currentJobCard);
} }
} }
std::cout << "Enter the job index to update: "; std::cout << "Select the Job Card to Update (Index): ";
util::read(choice); util::read(choice);
int selectedJobCardIndex = incompleteJobCards.find(choice); int selectedJobCardIndex = incompleteJobCards.find(choice);
if (selectedJobCardIndex != -1) if (selectedJobCardIndex != -1)
@@ -121,7 +121,7 @@ void TechnicianMenu::updateJobStatus()
std::string selectedJobID; std::string selectedJobID;
util::ServiceJobStatus selectedJobStatus = util::ServiceJobStatus::PENDING; util::ServiceJobStatus selectedJobStatus = util::ServiceJobStatus::PENDING;
util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser(); util::Map<std::string, const JobCard*> assignedJobCards = m_controller.getJobCardsByUser();
std::cout << "Select the type of job you want to update:\n1.Started\n2.In Progress\nChoice: "; std::cout << "Select the type of job you want to update\n1.Started\n2.Inprogress\nChoice: ";
util::read(choice); util::read(choice);
if (choice == 1) if (choice == 1)
{ {