Banking system

This commit is contained in:
Hladu357 2025-02-20 11:57:28 +01:00
commit f873d51127
2 changed files with 290 additions and 0 deletions

15
CT1/banking/Makefile Normal file
View File

@ -0,0 +1,15 @@
CXX = g++
CXXFLAGS = -std=c++20 -Wall -pedantic -O2
SRCS = $(wildcard *.cpp)
EXECS = $(SRCS:.cpp=)
all: $(EXECS)
%: %.cpp
$(CXX) $(CXXFLAGS) -o $@ $<
clean:
rm -f $(EXECS)
.PHONY: all clean

275
CT1/banking/banking.cpp Normal file
View File

@ -0,0 +1,275 @@
#include <stdexcept>
#include <cstdint>
#include <map>
#include <iostream>
#include <limits>
#include <sstream>
#define RESET "\033[0m"
#define RED "❌ \033[31m"
#define GREEN "✅ \033[32m"
#define BOLDWHITE "\033[1m\033[37m"
using money_t = int64_t;
using account_n = std::string;
constexpr size_t ACCOUNT_LEN = 4;
class Bank;
class Account;
enum class MenuState {
MENU,
VIEW = 1,
ADD_ACCOUNT = 2,
TRANSFER = 3,
DEPOSIT = 4,
WITHDRAW = 5,
EXIT = 6,
};
//######################################################
class BankingException : public std::exception {
public: const char* what() const noexcept override {
return "Internal exception!";
}
};
class InsufficientFundsException : public BankingException {
const char* what() const noexcept override {
return "Insufficient funds!";
}
};
class InvalidAmountException : public BankingException {
const char* what() const noexcept override {
return "Invalid amount!";
}
};
class InvalidAccountException : public BankingException {
const char* what() const noexcept override {
return "Invalid account number!";
}
};
class InvalidArgument : public BankingException {
const char* what() const noexcept override {
return "Invalid argument!";
}
};
//######################################################
std::istream& operator>>(std::istream& is, MenuState& state) {
int a;
if (!(is >> a)) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidArgument();
}
state = static_cast<MenuState>(a);
return is;
}
template <class T>
T getInput(std::istream& is) {
T n;
if (!(is >> n) || n < 0) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidAmountException();
}
return n;
}
template <>
account_n getInput<account_n>(std::istream& is) {
std::string s;
int64_t n;
if (!(is >> s)) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidAmountException();
}
std::istringstream iss(s);
if (!(iss >> n) || n < 0 || s.size() != ACCOUNT_LEN) {
is.clear();
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
throw InvalidAmountException();
}
return s;
}
constexpr const char* MENU_MSG = "=== Bank Account Management System ===\n1. View balance\n2. Create account\n3. Transfer funds\n4. Deposit funds\n5. Withdraw funds\n6. Exit\n";
//######################################################
class Account{
private:
money_t m_balance;
public:
Account(money_t m = 0) : m_balance(m) {}
~Account() = default;
money_t getBalance() {
return m_balance;
}
bool deposit(money_t m) {
if (m < 1) throw InvalidAmountException();
if (m + m_balance < m_balance) throw std::overflow_error("Account overflow");
m_balance += m;
return true;
}
bool withdraw(money_t m) {
if (m > m_balance) throw InsufficientFundsException();
m_balance -= m;
return true;
}
void setBalance(money_t m) {
m_balance = m;
}
};
//######################################################
class Bank
{
private:
std::map<account_n, Account> db;
public:
Bank() = default;
~Bank() = default;
account_n addAccount(account_n n = 0, money_t b = 0) {
if (n == "0000") n = db.size(); // generate account number
auto it = db.emplace(n, Account(b));
if( !it.second ) throw InvalidAccountException();
return it.first->first;
}
bool transfer(account_n from, account_n to, money_t amount) {
auto itFrom = db.find(from);
auto itTo = db.find(to);
money_t fromState;
if (itFrom == db.end() || itTo == db.end() || itFrom == itTo) throw InvalidAccountException();
fromState = itFrom->second.getBalance();
itFrom->second.withdraw(amount);
try {
itTo->second.deposit(amount);
} catch(std::exception& e) {
itFrom->second.setBalance(fromState);
throw;
}
return true;
}
bool deposit(account_n a, money_t m) {
auto it = db.find(a);
if (it == db.end()) throw InvalidAccountException();
return it->second.deposit(m);
}
bool withdraw(account_n a, money_t m) {
auto it = db.find(a);
if (it == db.end()) throw InvalidAccountException();
return it->second.withdraw(m);
}
money_t getBalance(account_n a) {
auto it = db.find(a);
if (it == db.end()) throw InvalidAccountException();
return it->second.getBalance();
}
};
//######################################################
int main(int ragc, char* argv[]) {
std::ostream& os = std::cout;
std::istream& is = std::cin;
MenuState state = MenuState::MENU;
Bank b;
for (;;) {
try {
account_n from = "0000";
account_n to = "0000";
money_t amount = 0;
os << BOLDWHITE << MENU_MSG << RESET;
is >> state;
switch (state) {
case MenuState::VIEW :
os << "Account number: ";
from = getInput<account_n>(is);
amount = b.getBalance(from);
os << GREEN << "Balance: " << "$" << amount << "\n" << RESET;
break;
case MenuState::TRANSFER :
os << "From account: ";
from = getInput<account_n>(is);
os << "To account: ";
to = getInput<account_n>(is);
os << "Amount: $";
amount = getInput<money_t>(is);
b.transfer(from, to, amount);
os << GREEN << "$" << amount << " transfered from " << from << " to " << to << "\n" << RESET;
break;
case MenuState::ADD_ACCOUNT :
os << "New account number (0 for auto): ";
from = getInput<account_n>(is);
os << "Starting balance: $";
amount = getInput<money_t>(is);
from = b.addAccount(from, amount);
os << GREEN << "Account " << from << " created with $" << amount << RESET;
break;
case MenuState::DEPOSIT :
os << "To account: ";
to = getInput<account_n>(is);
os << "Amount: $";
amount = getInput<money_t>(is);
b.deposit(to, amount);
os << GREEN << "$" << amount << " deposited\n" << RESET;
break;
case MenuState::WITHDRAW :
os << "From account: ";
from = getInput<account_n>(is);
os << "Amount: $";
amount = getInput<money_t>(is);
b.withdraw(from, amount);
os << GREEN << "$" << amount << " withdrawed\n" << RESET;
break;
case MenuState::EXIT :
os << "Exiting...\n";
return EXIT_SUCCESS;
break;
case MenuState::MENU :
default:
throw InvalidArgument();
}
} catch (BankingException& e) {
os << RED << e.what() << RESET;
// return EXIT_FAILURE;
}
state = MenuState::MENU;
os << "\n\n";
}
}