/* File: PaymentManagementService.cpp Description: Implements the PaymentManagementService class which manages payment-related operations in the Vehicle Service Management System. Provides functionality for attaching/detaching observers, sending notifications, and issuing payment reminders based on invoice status and thresholds. Author: Trenser Date: 20-May-2026 */ #include #include "Config.h" #include "Enums.h" #include "Factory.h" #include "FileManager.h" #include "InventoryItem.h" #include "Invoice.h" #include "JobCard.h" #include "PaymentManagementService.h" #include "Service.h" #include "ServiceBooking.h" #include "Timestamp.h" #include "User.h" #include "Utility.h" util::Map PaymentManagementService::m_observers{}; /* Function: attach Description: Attaches a user as an observer to the PaymentManagementService for receiving notifications. Parameters: - user: Pointer to the User object to be attached. Returns: - void */ void PaymentManagementService::attach(User* user) { if (user) { const std::string& userID = user->getId(); if (m_observers.find(userID) == -1) { m_observers[userID] = user; } } } /* Function: detach Description: Detaches a user from the observer list of the PaymentManagementService. Parameters: - user: Pointer to the User object to be detached. Returns: - void */ void PaymentManagementService::detach(User* user) { if (user) { const std::string& userID = user->getId(); if (m_observers.find(userID) != -1) { m_observers.remove(userID); } } } /* Function: sendNotification Description: Sends a notification to a user if they are registered as an observer. Parameters: - user: Pointer to the User object to receive the notification. - title: Title of the notification. - message: Message content of the notification. Returns: - void Throws: - std::runtime_error if notification creation fails. */ void PaymentManagementService::sendNotification(User* user, const std::string& title, const std::string& message) { if (user) { if (m_observers.find(user->getId()) != -1) { Notification* notification = Factory::getObject( user->getId(), user, title, message, util::Timestamp() ); if (notification) { user->addNotification(notification); } else { throw std::runtime_error("Failed to create notification"); } } } } /* Function: sendPaymentReminders Description: Iterates through all invoices in the datastore and sends payment reminders to customers whose invoices are pending beyond the configured threshold duration. Parameters: - None Returns: - void */ void PaymentManagementService::sendPaymentReminders() { auto& invoicesMap = m_dataStore.getInvoices(); int invoicesMapSize = invoicesMap.getSize(); for (int index = 0; index < invoicesMapSize; index++) { const Invoice* invoice = invoicesMap.getValueAt(index); if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING) { util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate(); util::Timestamp currentTimestamp; if (util::Timestamp::getDurationInHours(invoiceCreationTimestamp, currentTimestamp) >= config::threshold::PAYMENT_REMINDER_THRESHOLD_HOURS) { const ServiceBooking* serviceBooking = invoice->getBooking(); if (serviceBooking) { User* customer = serviceBooking->getCustomer(); if (customer) { std::string title = "Payment Reminder"; std::string message = "Your payment for Invoice ID " + invoice->getId() + " is still pending. Please complete the payment."; sendNotification(customer, title, message); } } } } } } /* Function: getObserverIDs Description: Retrieves the IDs of all observers currently attached to the PaymentManagementService. Parameters: - None Returns: - util::Vector: Vector of observer user IDs */ util::Vector PaymentManagementService::getObserverIDs() { util::Vector observerIDs; int numberOfObservers = m_observers.getSize(); for (int index = 0; index < numberOfObservers; index++) { User* observer = m_observers.getValueAt(index); if (observer) { observerIDs.push_back(observer->getId()); } } return observerIDs; } /* Function: loadInvoices Description: Loads invoices from persistent storage into the datastore. Validates associated service bookings and inventory parts before attaching them to each invoice. Throws exceptions if invalid IDs are encountered. Parameters: - None Returns: - void Throws: - std::runtime_error if a booking ID or part ID is invalid */ void PaymentManagementService::loadInvoices() { util::FileManager invoiceFileManager(config::file::INVOICE_FILE); auto& invoices = m_dataStore.getInvoices(); auto& serviceBookings = m_dataStore.getServiceBookings(); auto& inventoryItems = m_dataStore.getInventoryItems(); auto invoicesMap = invoiceFileManager.load(); for (int invoiceIndex = 0; invoiceIndex < invoicesMap.getSize(); invoiceIndex++) { Invoice* invoice = invoicesMap.getValueAt(invoiceIndex); int bookingIndex = serviceBookings.find(invoice->getBookingId()); if (bookingIndex == -1) { throw std::runtime_error("Invalid Booking ID"); } ServiceBooking* booking = serviceBookings.getValueAt(bookingIndex); invoice->setBooking(booking); util::Map invoiceParts; auto& partIDs = invoice->getPartIDs(); for (int partIndex = 0; partIndex < partIDs.getSize(); partIndex++) { const std::string& partID = partIDs[partIndex]; int inventoryIndex = inventoryItems.find(partID); if (inventoryIndex == -1) { throw std::runtime_error("Invalid Part ID"); } invoiceParts[partID] = inventoryItems.getValueAt(inventoryIndex); } invoice->setParts(invoiceParts); invoices[invoice->getId()] = invoice; } } /* Function: saveInvoices Description: Saves invoices from the datastore to persistent storage. Uses FileManager to serialize invoices into the configured file. Parameters: - None Returns: - void */ void PaymentManagementService::saveInvoices() { util::FileManager invoiceFileManager(config::file::INVOICE_FILE); auto& invoices = m_dataStore.getInvoices(); invoiceFileManager.save(invoices); } /* Function: loadObservers Description: Loads observer IDs from persistent storage and attaches corresponding users as observers to the PaymentManagementService. Parameters: - None Returns: - void Throws: - std::runtime_error if an observer ID is invalid (not found in datastore) */ void PaymentManagementService::loadObservers() { util::loadObservers(config::file::PAYMENTMANAGEMENTOBSERVERS, this, m_dataStore); } /* Function: saveObservers Description: Saves the current observer IDs of the PaymentManagementService to persistent storage for future retrieval. Parameters: - None Returns: - void */ void PaymentManagementService::saveObservers() { util::saveObservers(config::file::PAYMENTMANAGEMENTOBSERVERS, this); } /* Function: createInventoryItemsMap (static helper) Description: Builds a map of inventory items required for a given service and adds them to the booking’s inventory map. Parameters: - completeInventoryItemMapOfBooking: util::Map&, map to store inventory items for the booking - currentService: const Service*, pointer to the current service Returns: - void */ static void createInventoryItemsMap(util::Map& completeInventoryItemMapOfBooking, const Service* currentService) { auto& currentRequiredInventoryItems = currentService->getRequiredInventoryItems(); for (int iterator = 0; iterator < currentRequiredInventoryItems.getSize(); iterator++) { auto& currentRequiredInventoryItem = currentRequiredInventoryItems.getValueAt(iterator); completeInventoryItemMapOfBooking.insert(currentRequiredInventoryItem->getId(), currentRequiredInventoryItem); } } /* Function: generateInvoice Description: Generates an invoice for a completed service booking. Validates that all job cards are completed, calculates labor and parts cost, applies discount, and stores the invoice in the datastore. Parameters: - booking: ServiceBooking*, pointer to the service booking Returns: - void Throws: - std::runtime_error if booking is null or job cards are incomplete */ void PaymentManagementService::generateInvoice(ServiceBooking* booking) { if (!booking) { throw std::runtime_error("Invoice generation failed: booking is null."); } double totalLaborCost = 0, totalPartsCost = 0, totalServiceCost = 0; double discountPercentage = booking->getDiscountPercentage(); std::string bookingID = booking->getId(); util::Map servicesInTheBookedService = booking->getServices(); util::Map completeInventoryItemMapOfBooking; util::Map currentJobCards = m_dataStore.getJobCards(); for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++) { JobCard* currentJobCard = currentJobCards.getValueAt(iterator); 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 + "'."); } } for (int iterator = 0; iterator < servicesInTheBookedService.getSize(); iterator++) { Service* currentService = servicesInTheBookedService.getValueAt(iterator); if (currentService) { createInventoryItemsMap(completeInventoryItemMapOfBooking, currentService); totalLaborCost += currentService->getLaborCost(); totalPartsCost += util::calculatePartsCost(currentService); } } totalServiceCost = totalLaborCost + totalPartsCost; totalServiceCost -= (totalServiceCost * (discountPercentage / 100)); Invoice* invoice = Factory::getObject(bookingID, booking, util::Timestamp(), totalLaborCost, completeInventoryItemMapOfBooking, totalPartsCost, discountPercentage, totalServiceCost, util::Timestamp(), util::PaymentMode::NOTSET, util::PaymentStatus::PENDING); util::Map& currentInvoices = m_dataStore.getInvoices(); currentInvoices.insert(invoice->getId(), invoice); } /* Function: getInvoices Description: Retrieves all invoices associated with a specific customer. Parameters: - customerID: std::string, ID of the customer Returns: - util::Map containing the customer’s invoices */ util::Map PaymentManagementService::getInvoices(const std::string& customerID) { util::Map& currentInvoices = m_dataStore.getInvoices(); util::Map currentUserInvoices; for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++) { Invoice* currentInvoice = currentInvoices.getValueAt(iterator); if (currentInvoice->getBooking()->getCustomerId() == customerID) { currentUserInvoices.insert(currentInvoice->getId(), currentInvoice); } } return currentUserInvoices; } /* Function: completePayment Description: Completes payment for a specific invoice. Updates payment method, date, and status, then sends a notification to the customer. Parameters: - invoiceID: std::string, ID of the invoice - paymentMode: util::PaymentMode, mode of payment (e.g., ONLINE, OFFLINE) Returns: - void Throws: - std::runtime_error if the invoice ID is invalid */ void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode) { auto& currentInvoices = m_dataStore.getInvoices(); int invoiceIndex = currentInvoices.find(invoiceID); if (invoiceIndex != -1) { Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); if (invoice && invoice->getStatus() != util::PaymentStatus::PAID) { User* currentUser = invoice->getBooking()->getCustomer(); invoice->setPaymentMethod(paymentMode); invoice->setPaymentDate(util::Timestamp()); invoice->setStatus(util::PaymentStatus::PAID); std::string title, message; title = "Payment successful"; message = "Payment successful for Invoice ID " + invoiceID; sendNotification(currentUser, title, message); } } else { throw std::runtime_error("Payment failed: invalid invoice ID."); } } /* Function: getAllInvoice Description: Provides access to all invoices stored in the data store. Parameters: - none Returns: - util::Map&: Map of invoice IDs to invoice objects */ util::Map& PaymentManagementService::getAllInvoices() { return m_dataStore.getInvoices(); } /* Function: confirmPayment Description: Confirms payment for a specific invoice. Updates payment date and status, then sends a notification to the customer. Parameters: - invoiceID: std::string, ID of the invoice to confirm Returns: - void Throws: - std::runtime_error if the invoice ID is invalid */ void PaymentManagementService::confirmPayment(const std::string& invoiceID) { auto& currentInvoices = m_dataStore.getInvoices(); int invoiceIndex = currentInvoices.find(invoiceID); if (invoiceIndex == -1) { throw std::runtime_error("Payment confirmation failed: invalid invoice ID."); } Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); if (!invoice || invoice->getStatus() != util::PaymentStatus::PAID) { throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation."); } User* currentUser = invoice->getBooking()->getCustomer(); invoice->setStatus(util::PaymentStatus::COMPLETED); std::string title = "Payment Confirmed"; std::string message = "Payment Confirmed for Invoice ID " + invoiceID; sendNotification(currentUser, title, message); }