Banking system
This commit is contained in:
commit
f873d51127
|
@ -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
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue