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