Personal Budget Planner

Project Description
Create a C++ console application to help users manage their personal finances by recording income and expenses, categorizing transactions, and generating summary reports. The system should support the following features:
- Transaction Representation: Implement a Transaction class containing:
- transactionID (
int
): a unique identifier for each transaction. - date (
std::string
): date of the transaction (YYYY-MM-DD). - description (
std::string
): brief note (e.g., “Groceries”, “Salary”). - amount (
double
): positive for income, negative for expense. - category (
std::string
): classification (e.g., “Food”, “Utilities”, “Salary”). - Method display(): outputs transaction details.
- transactionID (
- BudgetManager: A BudgetManager class to manage transactions and summaries:
- addTransaction(date, desc, amount, category): registers a new transaction with a unique ID.
- removeTransaction(transactionID): deletes a transaction by its ID.
- getTransactionsByDate(date): returns all transactions on a given date.
- getTransactionsByCategory(category): returns all transactions in a specific category.
- generateMonthlyReport(month): calculates total income, total expenses, and net balance for a given month (format “YYYY-MM”).
- displayAllTransactions(): lists every recorded transaction.
- Behavioral Requirements:
- Validate inputs: dates must follow YYYY-MM-DD; amounts cannot be zero.
- Store transactions in a container of std::vector<std::unique_ptr> for exclusive ownership and automated cleanup.
- Assign transactionID automatically, incrementing with each addition.
Solution :
Transaction class
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <string>
#include <map>
// Transaction class models individual income or expense entries
class Transaction {
public:
int transactionID; // Unique identifier
std::string date; // YYYY-MM-DD
std::string description; // Note about transaction
double amount; // Positive = income, negative = expense
std::string category; // Category name
// Constructor initializes all fields
Transaction(int id, const std::string& d, const std::string& desc,
double amt, const std::string& cat)
: transactionID(id), date(d), description(desc), amount(amt), category(cat) {}
// Display transaction details
void display() const {
std::cout << "ID: " << transactionID
<< " | " << date
<< " | " << description
<< " | " << (amount < 0 ? "-" : "+") << std::abs(amount)
<< " | " << category << "\n";
}
};
BudgetManager class
// BudgetManager class handles all transactions and reporting
class BudgetManager {
std::vector<std::unique_ptr<Transaction>> transactions;
int nextID = 1; // Tracks next transactionID
public:
// Add a new transaction
void addTransaction(const std::string& date,
const std::string& desc,
double amount,
const std::string& category) {
if (date.empty() || desc.empty() || amount == 0.0) {
std::cerr << "Invalid transaction data\n";
return;
}
// Create and store new Transaction
transactions.push_back(
std::make_unique<Transaction>(nextID++, date, desc, amount, category)
);
std::cout << "Transaction added (ID: " << nextID - 1 << ")\n";
}
// Remove transaction by ID
bool removeTransaction(int id) {
auto it = std::remove_if(transactions.begin(), transactions.end(),
[&](const auto& t) { return t->transactionID == id; });
if (it == transactions.end()) {
std::cerr << "Transaction ID " << id << " not found\n";
return false;
}
transactions.erase(it, transactions.end());
std::cout << "Removed transaction ID " << id << "\n";
return true;
}
// Get all transactions on a specific date
std::vector<Transaction*> getTransactionsByDate(const std::string& date) const {
std::vector<Transaction*> result;
for (const auto& t : transactions) {
if (t->date == date) result.push_back(t.get());
}
return result;
}
// Get all transactions in a given category
std::vector<Transaction*> getTransactionsByCategory(const std::string& category) const {
std::vector<Transaction*> result;
for (const auto& t : transactions) {
if (t->category == category) result.push_back(t.get());
}
return result;
}
// Generate report for a specific month (YYYY-MM)
void generateMonthlyReport(const std::string& month) const {
double income = 0.0, expenses = 0.0;
for (const auto& t : transactions) {
if (t->date.rfind(month, 0) == 0) { // date starts with month
if (t->amount > 0) income += t->amount;
else expenses += std::abs(t->amount);
}
}
double net = income - expenses;
std::cout << "\nReport for " << month << ":\n"
<< "Total Income: +" << income << "\n"
<< "Total Expenses: -" << expenses << "\n"
<< "Net Balance: " << (net < 0 ? "-" : "+") << std::abs(net) << "\n";
}
// Display all recorded transactions
void displayAllTransactions() const {
std::cout << "\nAll Transactions:\n";
for (const auto& t : transactions) t->display();
}
};
Example usage
// Example usage
int main() {
BudgetManager bm;
bm.addTransaction("2025-05-01", "Salary", 3000.00, "Income"); // Income
bm.addTransaction("2025-05-02", "Rent", -1200.00, "Housing"); // Expense
bm.addTransaction("2025-05-03", "Groceries", -150.45, "Food");
bm.displayAllTransactions();
bm.generateMonthlyReport("2025-05");
bm.removeTransaction(2); // Remove rent
bm.displayAllTransactions();
return 0;
}
Detailed Explanation of Logic
The BudgetManager stores each Transaction in a std::vector<std::unique_ptr<Transaction>>
, ensuring exclusive ownership and automatic cleanup. Every addition increments nextID to assign unique transactionIDs.
- Adding Transactions: Validates nonempty date and description, and nonzero amount. Creates a new Transaction via
std::make_unique
, then pushes it onto the vector. - Removing Transactions: Uses
std::remove_if
to locate entries matching the given transactionID, erases them, and reports success or failure. - Querying:
getTransactionsByDate
andgetTransactionsByCategory
iterate over the vector, compare the relevant field, and return rawTransaction*
pointers for non-owning access. - Reporting:
generateMonthlyReport
checks each date string prefix against the target month (rfind(month, 0)
), sums positive amounts as income and absolute values of negatives as expenses, then computes and displays net balance. - Display Routine:
displayAllTransactions
loops through every stored pointer, invoking display() for clean output.