From 7047e96b3cc6120496a149d6f77225516e3616b0 Mon Sep 17 00:00:00 2001 From: Jissin Mathew Date: Mon, 15 Jun 2026 12:43:44 +0530 Subject: [PATCH] Implement Service Refactoring 1950: Implement Service Refactorings UserStory #1950 1. Added `DataStoreLockGuard` integration in `PaymentManagementService` methods (`sendPaymentReminders`, `generateInvoice`, `getInvoices`, `completePayment`, `getAllInvoices`, `confirmPayment`) to ensure thread-safe access to the datastore. 2. Implemented record enrichment logic in `DataStore::getInvoices()` to automatically link `Invoice` objects with their corresponding `ServiceBooking` and `InventoryItem` entities during loading, validating relationships and throwing exceptions for invalid references. 3. Refactored invoice persistence by implementing `saveInvoices()` using `saveRecords` to write tracked records directly to shared memory. 4. Updated invoice creation and modification flows: - `generateInvoice`: Uses `createNewRecord` to insert new invoices into the tracked cache and triggers immediate persistence. - `completePayment` & `confirmPayment`: Marks records as `MODIFIED` in the `TrackedRecord` state before saving changes. 5. Updated data access patterns across `PaymentManagementService` to use `.data` pointers from `TrackedRecord` objects returned by the datastore, replacing direct pointer access to mapped objects. 6. Added necessary header dependencies including `DataStoreLockGuard.h`, `Invoice.h`, and `SerializedRecords.h` to support locking mechanisms and structured serialization. N/A Sreeja Reghukumar, please review --- .../Trenser.VehicleServiceSystem.vcxproj | 1 + .../datastores/DataStore.cpp | 41 +++++++++++++++ .../datastores/DataStoreLockGuard.h | 28 ++++++++++ .../services/PaymentManagementService.cpp | 52 ++++++++++++------- 4 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj index 6a269f5..7c4bfd3 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem.vcxproj @@ -157,6 +157,7 @@ + diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp index af6fdc6..3092551 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStore.cpp @@ -12,6 +12,7 @@ Date: 19-May-2026 #include "Config.h" #include "SerializedRecords.h" #include "FileHelper.h" +#include "Invoice.h" /* Function: DataStore @@ -319,6 +320,45 @@ Returns: */ util::Map>& DataStore::getInvoices() { + auto& serviceBookings = getServiceBookings(); + auto& inventoryItems = getInventoryItems(); + util::Map> invoices = loadRecords(m_invoices); + refreshCache(m_invoiceCache, invoices); + for (int iterator = 0; iterator < m_invoiceCache.getSize(); iterator++) + { + auto& trackedInvoice = m_invoiceCache.getValueAt(iterator); + Invoice* invoice = trackedInvoice.data; + if (!invoice) + { + continue; + } + const std::string& currentBookingId = invoice->getBookingId(); + int currentBookingIndex = serviceBookings.find(currentBookingId); + if (currentBookingIndex == -1) + { + throw std::runtime_error("Invalid Service Booking Index."); + } + ServiceBooking* currentBooking = serviceBookings.getValueAt(currentBookingIndex).data; + auto& currentInventoryItemIds = invoice->getPartIDs(); + util::Map currentInventoryItems; + for (int iterator = 0; iterator < currentInventoryItemIds.getSize(); iterator++) + { + const std::string& currentItemId = currentInventoryItemIds[iterator]; + int currentItemIndex = inventoryItems.find(currentItemId); + if (currentItemIndex == -1) + { + throw std::runtime_error("Invalid Inventory item id."); + } + InventoryItem* currentItem = inventoryItems.getValueAt(currentItemIndex).data; + if (!currentItem) + { + continue; + } + currentInventoryItems[currentItemId] = currentItem; + } + invoice->setBooking(currentBooking); + invoice->setParts(currentInventoryItems); + } return m_invoiceCache; } @@ -455,6 +495,7 @@ Returns: */ void DataStore::saveInvoices() { + saveRecords(m_invoices, m_invoiceCache); } /* diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h new file mode 100644 index 0000000..2e04eb0 --- /dev/null +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/datastores/DataStoreLockGuard.h @@ -0,0 +1,28 @@ +/* +File: DataStoreLockGuard.h +Description: Defines the DataStoreLockGuard class used to manage DataStore + locking and unlocking automatically within a scope. +Author: Trenser +Date: 12-June-2026 +*/ + +#pragma once +#include "DataStore.h" + +class DataStoreLockGuard +{ +public: + explicit DataStoreLockGuard(DataStore& dataStore) + : m_dataStore(dataStore) + { + m_dataStore.lockDataStore(); + } + ~DataStoreLockGuard() + { + m_dataStore.unlockDataStore(); + } + DataStoreLockGuard(const DataStoreLockGuard&) = delete; + DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete; +private: + DataStore& m_dataStore; +}; \ No newline at end of file diff --git a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp index 1fc1ff8..45f1742 100644 --- a/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp +++ b/Trenser.VehicleServiceSystem/Trenser.VehicleServiceSystem/services/PaymentManagementService.cpp @@ -16,6 +16,7 @@ Date: 20-May-2026 #include "Invoice.h" #include "JobCard.h" #include "PaymentManagementService.h" +#include "DataStoreLockGuard.h" #include "Service.h" #include "ServiceBooking.h" #include "Timestamp.h" @@ -113,11 +114,12 @@ Returns: */ void PaymentManagementService::sendPaymentReminders() { - auto& invoicesMap = m_dataStore.getInvoices(); - int invoicesMapSize = invoicesMap.getSize(); + DataStoreLockGuard lock(m_dataStore); + auto& trackedInvoicesMap = m_dataStore.getInvoices(); + int invoicesMapSize = trackedInvoicesMap.getSize(); for (int index = 0; index < invoicesMapSize; index++) { - const Invoice* invoice = invoicesMap.getValueAt(index); + const Invoice* invoice = trackedInvoicesMap.getValueAt(index).data; if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING) { util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate(); @@ -290,6 +292,7 @@ Throws: */ void PaymentManagementService::generateInvoice(ServiceBooking* booking) { + DataStoreLockGuard lock(m_dataStore); if (!booking) { throw std::runtime_error("Invoice generation failed: booking is null."); @@ -299,10 +302,10 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking) 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++) + auto& currentTrackedJobCards = m_dataStore.getJobCards(); + for (int iterator = 0; iterator < currentTrackedJobCards.getSize(); iterator++) { - JobCard* currentJobCard = currentJobCards.getValueAt(iterator); + JobCard* currentJobCard = currentTrackedJobCards.getValueAt(iterator).data; util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus(); if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED) { @@ -322,8 +325,9 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking) 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); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); + currentTrackedInvoices.insert(invoice->getId(), util::createNewRecord(invoice)); + m_dataStore.saveInvoices(); } /* @@ -336,11 +340,12 @@ Returns: */ util::Map PaymentManagementService::getInvoices(const std::string& customerID) { - util::Map& currentInvoices = m_dataStore.getInvoices(); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); util::Map currentUserInvoices; - for (int iterator = 0; iterator < currentInvoices.getSize(); iterator++) + for (int iterator = 0; iterator < currentTrackedInvoices.getSize(); iterator++) { - Invoice* currentInvoice = currentInvoices.getValueAt(iterator); + Invoice* currentInvoice = currentTrackedInvoices.getValueAt(iterator).data; if (currentInvoice->getBooking()->getCustomerId() == customerID) { currentUserInvoices.insert(currentInvoice->getId(), currentInvoice); @@ -363,11 +368,13 @@ Throws: */ void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode) { - auto& currentInvoices = m_dataStore.getInvoices(); - int invoiceIndex = currentInvoices.find(invoiceID); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); + int invoiceIndex = currentTrackedInvoices.find(invoiceID); if (invoiceIndex != -1) { - Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); + auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex); + Invoice* invoice = trackedInvoice.data; if (invoice && invoice->getStatus() != util::PaymentStatus::PAID) { User* currentUser = invoice->getBooking()->getCustomer(); @@ -378,12 +385,14 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti title = "Payment successful"; message = "Payment successful for Invoice ID " + invoiceID; sendNotification(currentUser, title, message); + trackedInvoice.state = RecordState::MODIFIED; } } else { throw std::runtime_error("Payment failed: invalid invoice ID."); } + m_dataStore.saveInvoices(); } /* @@ -396,7 +405,10 @@ Returns: */ util::Map& PaymentManagementService::getAllInvoices() { - return m_dataStore.getInvoices(); + DataStoreLockGuard lock(m_dataStore); + util::Map invoices; + invoices = util::getObjects(m_dataStore.getInvoices()); + return invoices; } /* @@ -412,20 +424,24 @@ Throws: */ void PaymentManagementService::confirmPayment(const std::string& invoiceID) { - auto& currentInvoices = m_dataStore.getInvoices(); - int invoiceIndex = currentInvoices.find(invoiceID); + DataStoreLockGuard lock(m_dataStore); + auto& currentTrackedInvoices = m_dataStore.getInvoices(); + int invoiceIndex = currentTrackedInvoices.find(invoiceID); if (invoiceIndex == -1) { throw std::runtime_error("Payment confirmation failed: invalid invoice ID."); } - Invoice* invoice = currentInvoices.getValueAt(invoiceIndex); + auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex); + Invoice* invoice = trackedInvoice.data; 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); + trackedInvoice.state = RecordState::MODIFIED; std::string title = "Payment Confirmed"; std::string message = "Payment Confirmed for Invoice ID " + invoiceID; sendNotification(currentUser, title, message); + m_dataStore.saveInvoices(); } \ No newline at end of file