Implement Service Refactoring
<UserStory> 1950: Implement Service Refactorings </UserStory> UserStory #1950 <Changes> 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<Invoice, SerializedInvoice>` 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. </Changes> <Test> N/A </Test> <Review> Sreeja Reghukumar, please review </Review>
This commit is contained in:
+1
@@ -157,6 +157,7 @@
|
|||||||
<ClInclude Include="core\patterns\Observer.h" />
|
<ClInclude Include="core\patterns\Observer.h" />
|
||||||
<ClInclude Include="core\patterns\Subject.h" />
|
<ClInclude Include="core\patterns\Subject.h" />
|
||||||
<ClInclude Include="datastores\DataStore.h" />
|
<ClInclude Include="datastores\DataStore.h" />
|
||||||
|
<ClInclude Include="datastores\DataStoreLockGuard.h" />
|
||||||
<ClInclude Include="datastores\sharedmemory\FileHeader.h" />
|
<ClInclude Include="datastores\sharedmemory\FileHeader.h" />
|
||||||
<ClInclude Include="datastores\sharedmemory\MappingInfo.h" />
|
<ClInclude Include="datastores\sharedmemory\MappingInfo.h" />
|
||||||
<ClInclude Include="datastores\sharedmemory\RecordState.h" />
|
<ClInclude Include="datastores\sharedmemory\RecordState.h" />
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Date: 19-May-2026
|
|||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "SerializedRecords.h"
|
#include "SerializedRecords.h"
|
||||||
#include "FileHelper.h"
|
#include "FileHelper.h"
|
||||||
|
#include "Invoice.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Function: DataStore
|
Function: DataStore
|
||||||
@@ -319,6 +320,45 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
util::Map<std::string, TrackedRecord<Invoice>>& DataStore::getInvoices()
|
util::Map<std::string, TrackedRecord<Invoice>>& DataStore::getInvoices()
|
||||||
{
|
{
|
||||||
|
auto& serviceBookings = getServiceBookings();
|
||||||
|
auto& inventoryItems = getInventoryItems();
|
||||||
|
util::Map<std::string, TrackedRecord<Invoice>> invoices = loadRecords<Invoice, SerializedInvoice>(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<std::string, InventoryItem*> 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;
|
return m_invoiceCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,6 +495,7 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
void DataStore::saveInvoices()
|
void DataStore::saveInvoices()
|
||||||
{
|
{
|
||||||
|
saveRecords<Invoice, SerializedInvoice>(m_invoices, m_invoiceCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+28
@@ -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;
|
||||||
|
};
|
||||||
+34
-18
@@ -16,6 +16,7 @@ Date: 20-May-2026
|
|||||||
#include "Invoice.h"
|
#include "Invoice.h"
|
||||||
#include "JobCard.h"
|
#include "JobCard.h"
|
||||||
#include "PaymentManagementService.h"
|
#include "PaymentManagementService.h"
|
||||||
|
#include "DataStoreLockGuard.h"
|
||||||
#include "Service.h"
|
#include "Service.h"
|
||||||
#include "ServiceBooking.h"
|
#include "ServiceBooking.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
@@ -113,11 +114,12 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
void PaymentManagementService::sendPaymentReminders()
|
void PaymentManagementService::sendPaymentReminders()
|
||||||
{
|
{
|
||||||
auto& invoicesMap = m_dataStore.getInvoices();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
int invoicesMapSize = invoicesMap.getSize();
|
auto& trackedInvoicesMap = m_dataStore.getInvoices();
|
||||||
|
int invoicesMapSize = trackedInvoicesMap.getSize();
|
||||||
for (int index = 0; index < invoicesMapSize; index++)
|
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)
|
if (invoice && invoice->getStatus() == util::PaymentStatus::PENDING)
|
||||||
{
|
{
|
||||||
util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate();
|
util::Timestamp invoiceCreationTimestamp = invoice->getInvoiceDate();
|
||||||
@@ -290,6 +292,7 @@ Throws:
|
|||||||
*/
|
*/
|
||||||
void PaymentManagementService::generateInvoice(ServiceBooking* booking)
|
void PaymentManagementService::generateInvoice(ServiceBooking* booking)
|
||||||
{
|
{
|
||||||
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
if (!booking)
|
if (!booking)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invoice generation failed: booking is null.");
|
throw std::runtime_error("Invoice generation failed: booking is null.");
|
||||||
@@ -299,10 +302,10 @@ void PaymentManagementService::generateInvoice(ServiceBooking* booking)
|
|||||||
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();
|
||||||
util::Map<std::string, InventoryItem*> completeInventoryItemMapOfBooking;
|
util::Map<std::string, InventoryItem*> completeInventoryItemMapOfBooking;
|
||||||
util::Map<std::string, JobCard*> currentJobCards = m_dataStore.getJobCards();
|
auto& currentTrackedJobCards = m_dataStore.getJobCards();
|
||||||
for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++)
|
for (int iterator = 0; iterator < currentTrackedJobCards.getSize(); iterator++)
|
||||||
{
|
{
|
||||||
JobCard* currentJobCard = currentJobCards.getValueAt(iterator);
|
JobCard* currentJobCard = currentTrackedJobCards.getValueAt(iterator).data;
|
||||||
util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus();
|
util::ServiceJobStatus currentJobCardStatus = currentJobCard->getStatus();
|
||||||
if (currentJobCard->getBookingId() == bookingID && currentJobCardStatus != util::ServiceJobStatus::CANCELLED && currentJobCardStatus != util::ServiceJobStatus::COMPLETED)
|
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 = totalLaborCost + totalPartsCost;
|
||||||
totalServiceCost -= (totalServiceCost * (discountPercentage / 100));
|
totalServiceCost -= (totalServiceCost * (discountPercentage / 100));
|
||||||
Invoice* invoice = Factory::getObject<Invoice>(bookingID, booking, util::Timestamp(), totalLaborCost, 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();
|
auto& currentTrackedInvoices = m_dataStore.getInvoices();
|
||||||
currentInvoices.insert(invoice->getId(), invoice);
|
currentTrackedInvoices.insert(invoice->getId(), util::createNewRecord(invoice));
|
||||||
|
m_dataStore.saveInvoices();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -336,11 +340,12 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
util::Map<std::string, Invoice*> PaymentManagementService::getInvoices(const std::string& customerID)
|
util::Map<std::string, Invoice*> PaymentManagementService::getInvoices(const std::string& customerID)
|
||||||
{
|
{
|
||||||
util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
|
auto& currentTrackedInvoices = m_dataStore.getInvoices();
|
||||||
util::Map<std::string, Invoice*> currentUserInvoices;
|
util::Map<std::string, Invoice*> 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)
|
if (currentInvoice->getBooking()->getCustomerId() == customerID)
|
||||||
{
|
{
|
||||||
currentUserInvoices.insert(currentInvoice->getId(), currentInvoice);
|
currentUserInvoices.insert(currentInvoice->getId(), currentInvoice);
|
||||||
@@ -363,11 +368,13 @@ Throws:
|
|||||||
*/
|
*/
|
||||||
void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode)
|
void PaymentManagementService::completePayment(const std::string& invoiceID, util::PaymentMode paymentMode)
|
||||||
{
|
{
|
||||||
auto& currentInvoices = m_dataStore.getInvoices();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
int invoiceIndex = currentInvoices.find(invoiceID);
|
auto& currentTrackedInvoices = m_dataStore.getInvoices();
|
||||||
|
int invoiceIndex = currentTrackedInvoices.find(invoiceID);
|
||||||
if (invoiceIndex != -1)
|
if (invoiceIndex != -1)
|
||||||
{
|
{
|
||||||
Invoice* invoice = currentInvoices.getValueAt(invoiceIndex);
|
auto& trackedInvoice = currentTrackedInvoices.getValueAt(invoiceIndex);
|
||||||
|
Invoice* invoice = trackedInvoice.data;
|
||||||
if (invoice && invoice->getStatus() != util::PaymentStatus::PAID)
|
if (invoice && invoice->getStatus() != util::PaymentStatus::PAID)
|
||||||
{
|
{
|
||||||
User* currentUser = invoice->getBooking()->getCustomer();
|
User* currentUser = invoice->getBooking()->getCustomer();
|
||||||
@@ -378,12 +385,14 @@ void PaymentManagementService::completePayment(const std::string& invoiceID, uti
|
|||||||
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);
|
||||||
|
trackedInvoice.state = RecordState::MODIFIED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Payment failed: invalid invoice ID.");
|
throw std::runtime_error("Payment failed: invalid invoice ID.");
|
||||||
}
|
}
|
||||||
|
m_dataStore.saveInvoices();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -396,7 +405,10 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
util::Map<std::string, Invoice*>& PaymentManagementService::getAllInvoices()
|
util::Map<std::string, Invoice*>& PaymentManagementService::getAllInvoices()
|
||||||
{
|
{
|
||||||
return m_dataStore.getInvoices();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
|
util::Map<std::string, Invoice*> invoices;
|
||||||
|
invoices = util::getObjects(m_dataStore.getInvoices());
|
||||||
|
return invoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -412,20 +424,24 @@ Throws:
|
|||||||
*/
|
*/
|
||||||
void PaymentManagementService::confirmPayment(const std::string& invoiceID)
|
void PaymentManagementService::confirmPayment(const std::string& invoiceID)
|
||||||
{
|
{
|
||||||
auto& currentInvoices = m_dataStore.getInvoices();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
int invoiceIndex = currentInvoices.find(invoiceID);
|
auto& currentTrackedInvoices = m_dataStore.getInvoices();
|
||||||
|
int invoiceIndex = currentTrackedInvoices.find(invoiceID);
|
||||||
if (invoiceIndex == -1)
|
if (invoiceIndex == -1)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Payment confirmation failed: invalid invoice ID.");
|
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)
|
if (!invoice || invoice->getStatus() != util::PaymentStatus::PAID)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation.");
|
throw std::runtime_error("Payment confirmation failed: invoice is not awaiting confirmation.");
|
||||||
}
|
}
|
||||||
User* currentUser = invoice->getBooking()->getCustomer();
|
User* currentUser = invoice->getBooking()->getCustomer();
|
||||||
invoice->setStatus(util::PaymentStatus::COMPLETED);
|
invoice->setStatus(util::PaymentStatus::COMPLETED);
|
||||||
|
trackedInvoice.state = RecordState::MODIFIED;
|
||||||
std::string title = "Payment Confirmed";
|
std::string title = "Payment Confirmed";
|
||||||
std::string message = "Payment Confirmed for Invoice ID " + invoiceID;
|
std::string message = "Payment Confirmed for Invoice ID " + invoiceID;
|
||||||
sendNotification(currentUser, title, message);
|
sendNotification(currentUser, title, message);
|
||||||
|
m_dataStore.saveInvoices();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user