Implement Generate Invoice

<UserStory> PAY001: Generate Invoice </UserStory>

<Changes>
    1. Added Utility.h to project configuration for supporting invoice generation utilities.
    2. Updated Invoice model to use string-based keys for parts mapping instead of integer keys.
    3. Implemented PaymentManagementService::generateInvoice to aggregate labour cost, parts cost, and apply discounts.
    4. Integrated invoice creation with Factory to instantiate Invoice objects and persist them into datastore.
    5. Enhanced Enums with PaymentMode::NOTSET to handle default invoice state.
</Changes>

<Test>

 Acceptance Criteria:
 1. Invoice auto-generates for each service booking once jobs are completed.
 2. Invoice shows a clear breakdown of charges including labour cost, parts cost, discount, and total amount.

 Precondition:
  1. Service booking exists with at least one service and required inventory items.
  2. Datastore is available for storing invoices.
  3. Payment mode and status enums are properly configured.

 Steps:
  1. Complete all jobs in a service booking.
    - Verify that PaymentManagementService::generateInvoice is triggered.
  2. Check datastore for newly created invoice.
    - Verify that invoice contains booking ID, labour cost, parts cost, discount, and total amount.
  3. Inspect invoice details.
    - Verify that breakdown of charges is accurate and discount is applied correctly.
  4. Confirm invoice status.
    - Verify that invoice is created with PaymentMode::NOTSET and PaymentStatus::PENDING.
</Test>

<Review>
Sreeja Reghukumar, please review
</Review>
This commit is contained in:
Jissin Mathew
2026-05-20 14:57:10 +05:30
parent 56c5c2dc70
commit 61f70a54f6
9 changed files with 97 additions and 18 deletions
@@ -176,6 +176,7 @@
<ClInclude Include="utilities\Map.h" /> <ClInclude Include="utilities\Map.h" />
<ClInclude Include="utilities\OutputHelper.h" /> <ClInclude Include="utilities\OutputHelper.h" />
<ClInclude Include="utilities\Timestamp.h" /> <ClInclude Include="utilities\Timestamp.h" />
<ClInclude Include="utilities\Utility.h" />
<ClInclude Include="utilities\Validator.h" /> <ClInclude Include="utilities\Validator.h" />
<ClInclude Include="utilities\Vector.h" /> <ClInclude Include="utilities\Vector.h" />
<ClInclude Include="views\AdminMenu.h" /> <ClInclude Include="views\AdminMenu.h" />
@@ -233,5 +233,8 @@
<ClInclude Include="models\ComboPackage.h"> <ClInclude Include="models\ComboPackage.h">
<Filter>Header Files\Models</Filter> <Filter>Header Files\Models</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="utilities\Utility.h">
<Filter>Header Files\Utilities</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -16,7 +16,8 @@ Invoice::Invoice(
const std::string& bookingId, const std::string& bookingId,
ServiceBooking* booking, ServiceBooking* booking,
const util::Timestamp& invoiceDate, const util::Timestamp& invoiceDate,
double laborCost, const util::Map<int, double laborCost,
const util::Map<std::string,
InventoryItem*>& parts, InventoryItem*>& parts,
double partsCost, double partsCost,
double discountPercentage, double discountPercentage,
@@ -63,7 +64,7 @@ double Invoice::getLaborCost() const
return m_laborCost; return m_laborCost;
} }
const util::Map<int, InventoryItem*>& Invoice::getParts() const const util::Map<std::string, InventoryItem*>& Invoice::getParts() const
{ {
return m_parts; return m_parts;
} }
@@ -123,7 +124,7 @@ void Invoice::setLaborCost(double laborCost)
m_laborCost = laborCost; m_laborCost = laborCost;
} }
void Invoice::setParts(const util::Map<int, InventoryItem*>& parts) void Invoice::setParts(const util::Map<std::string, InventoryItem*>& parts)
{ {
m_parts = parts; m_parts = parts;
} }
@@ -16,7 +16,7 @@ private:
ServiceBooking* m_booking; ServiceBooking* m_booking;
util::Timestamp m_invoiceDate; util::Timestamp m_invoiceDate;
double m_laborCost; double m_laborCost;
util::Map<int, InventoryItem*> m_parts; util::Map<std::string, InventoryItem*> m_parts;
double m_partsCost; double m_partsCost;
double m_discountPercentage; double m_discountPercentage;
double m_totalAmount; double m_totalAmount;
@@ -30,8 +30,8 @@ public:
const std::string& bookingId, const std::string& bookingId,
ServiceBooking* booking, ServiceBooking* booking,
const util::Timestamp& invoiceDate, const util::Timestamp& invoiceDate,
double laborCost, const util::Map<int, double laborCost,
InventoryItem*>& parts, const util::Map<std::string,InventoryItem*>& parts,
double partsCost, double partsCost,
double discountPercentage, double discountPercentage,
double totalAmount, double totalAmount,
@@ -44,7 +44,7 @@ public:
ServiceBooking* getBooking() const; ServiceBooking* getBooking() const;
const util::Timestamp& getInvoiceDate() const; const util::Timestamp& getInvoiceDate() const;
double getLaborCost() const; double getLaborCost() const;
const util::Map<int, InventoryItem*>& getParts() const; const util::Map<std::string, InventoryItem*>& getParts() const;
double getPartsCost() const; double getPartsCost() const;
double getDiscountPercentage() const; double getDiscountPercentage() const;
double getTotalAmount() const; double getTotalAmount() const;
@@ -56,7 +56,7 @@ public:
void setBooking(ServiceBooking* booking); void setBooking(ServiceBooking* booking);
void setInvoiceDate(const util::Timestamp& invoiceDate); void setInvoiceDate(const util::Timestamp& invoiceDate);
void setLaborCost(double laborCost); void setLaborCost(double laborCost);
void setParts(const util::Map<int, InventoryItem*>& parts); void setParts(const util::Map<std::string, InventoryItem*>& parts);
void setPartsCost(double partsCost); void setPartsCost(double partsCost);
void setDiscountPercentage(double discountPercentage); void setDiscountPercentage(double discountPercentage);
void setTotalAmount(double totalAmount); void setTotalAmount(double totalAmount);
@@ -7,7 +7,7 @@ JobCard::JobCard()
m_booking(nullptr), m_booking(nullptr),
m_service(nullptr), m_service(nullptr),
m_technician(nullptr), m_technician(nullptr),
m_status(ServiceJobStatus()) {} m_status(util::ServiceJobStatus()) {}
JobCard::JobCard(const std::string& bookingId, JobCard::JobCard(const std::string& bookingId,
ServiceBooking* booking, ServiceBooking* booking,
@@ -16,7 +16,7 @@ JobCard::JobCard(const std::string& bookingId,
const std::string& technicianId, const std::string& technicianId,
User* technician, User* technician,
const util::Timestamp& assignedDate, const util::Timestamp& assignedDate,
ServiceJobStatus status, util::ServiceJobStatus status,
const util::Timestamp& completionDate const util::Timestamp& completionDate
) )
: m_id("JC" + std::to_string(++m_uid)), : m_id("JC" + std::to_string(++m_uid)),
@@ -70,7 +70,7 @@ const util::Timestamp& JobCard::getAssignedDate() const
return m_assignedDate; return m_assignedDate;
} }
ServiceJobStatus JobCard::getStatus() const util::ServiceJobStatus JobCard::getStatus() const
{ {
return m_status; return m_status;
} }
@@ -120,7 +120,7 @@ void JobCard::setAssignedDate(const util::Timestamp& assignedDate)
m_assignedDate = assignedDate; m_assignedDate = assignedDate;
} }
void JobCard::setStatus(ServiceJobStatus status) void JobCard::setStatus(util::ServiceJobStatus status)
{ {
m_status = status; m_status = status;
} }
@@ -1,12 +1,12 @@
#pragma once #pragma once
#include <string> #include <string>
#include "Enums.h"
#include "Timestamp.h" #include "Timestamp.h"
class ServiceBooking; class ServiceBooking;
class Service; class Service;
class User; class User;
enum class ServiceJobStatus : int;
class JobCard class JobCard
{ {
@@ -20,7 +20,7 @@ private:
std::string m_technicianId; std::string m_technicianId;
User* m_technician; User* m_technician;
util::Timestamp m_assignedDate; util::Timestamp m_assignedDate;
ServiceJobStatus m_status; util::ServiceJobStatus m_status;
util::Timestamp m_completionDate; util::Timestamp m_completionDate;
public: public:
@@ -32,7 +32,7 @@ public:
const std::string& technicianId, const std::string& technicianId,
User* technician, User* technician,
const util::Timestamp& assignedDate, const util::Timestamp& assignedDate,
ServiceJobStatus status, util::ServiceJobStatus status,
const util::Timestamp& completionDate const util::Timestamp& completionDate
); );
const std::string& getId() const; const std::string& getId() const;
@@ -43,7 +43,7 @@ public:
const std::string& getTechnicianId() const; const std::string& getTechnicianId() const;
User* getTechnician() const; User* getTechnician() const;
const util::Timestamp& getAssignedDate() const; const util::Timestamp& getAssignedDate() const;
ServiceJobStatus getStatus() const; util::ServiceJobStatus getStatus() const;
const util::Timestamp& getCompletionDate() const; const util::Timestamp& getCompletionDate() const;
void setId(const std::string& id); void setId(const std::string& id);
void setBookingId(const std::string& bookingId); void setBookingId(const std::string& bookingId);
@@ -53,6 +53,6 @@ public:
void setTechnicianId(const std::string& technicianId); void setTechnicianId(const std::string& technicianId);
void setTechnician(User* technician); void setTechnician(User* technician);
void setAssignedDate(const util::Timestamp& assignedDate); void setAssignedDate(const util::Timestamp& assignedDate);
void setStatus(ServiceJobStatus status); void setStatus(util::ServiceJobStatus status);
void setCompletionDate(const util::Timestamp& completionDate); void setCompletionDate(const util::Timestamp& completionDate);
}; };
@@ -1 +1,57 @@
#include "PaymentManagementService.h" #include "PaymentManagementService.h"
#include "ServiceBooking.h"
#include "Service.h"
#include "InventoryItem.h"
#include "Utility.h"
#include "Factory.h"
#include "Timestamp.h"
#include "Invoice.h"
#include "JobCard.h"
#include "Enums.h"
static void createInventoryItemsMap(util::Map<std::string, InventoryItem*>& 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);
}
}
void PaymentManagementService::generateInvoice(ServiceBooking* booking)
{
if (!booking)
{
throw std::runtime_error("Invoice generation failed: booking is null.");
}
double totalLabourCost = 0, totalPartsCost = 0, totalServiceCost = 0;
double discountPercentage = booking->getDiscountPercentage();
std::string bookingID = booking->getId();
util::Map<std::string, Service*> servicesInTheBookedService = booking->getServices();
util::Map<std::string, InventoryItem*> completeInventoryItemMapOfBooking;
util::Map<std::string, JobCard*> currentJobCards = m_dataStore.getJobCards();
for (int iterator = 0; iterator < currentJobCards.getSize(); iterator++)
{
JobCard* currentJobCard = currentJobCards.getValueAt(iterator);
if (currentJobCard->getBookingId() == bookingID && currentJobCard->getStatus() != 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);
totalLabourCost += currentService->getLaborCost();
totalPartsCost += calculatePartsCost(currentService);
}
}
totalServiceCost = totalLabourCost + totalPartsCost;
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);
util::Map<std::string, Invoice*>& currentInvoices = m_dataStore.getInvoices();
currentInvoices.insert(invoice->getId(), invoice);
}
@@ -13,7 +13,8 @@ namespace util
enum class PaymentMode enum class PaymentMode
{ {
ONLINE, ONLINE,
OFFLINE OFFLINE,
NOTSET
}; };
enum class PaymentStatus enum class PaymentStatus
@@ -73,6 +74,8 @@ namespace util
return "ONLINE"; return "ONLINE";
case PaymentMode::OFFLINE: case PaymentMode::OFFLINE:
return "OFFLINE"; return "OFFLINE";
case PaymentMode::NOTSET:
return "NOTSET";
} }
throw std::invalid_argument("Invalid PaymentMode"); throw std::invalid_argument("Invalid PaymentMode");
} }
@@ -0,0 +1,15 @@
#pragma once
#include "Service.h"
#include "InventoryItem.h"
inline double calculatePartsCost(const Service* service)
{
double cost = 0;
auto& requiredInventoryItems = service->getRequiredInventoryItems();
int requiredInventoryItemsSize = requiredInventoryItems.getSize();
for (int index = 0; index < requiredInventoryItemsSize; index++)
{
cost += requiredInventoryItems.getValueAt(index)->getPrice();
}
return cost;
}