xxx

🧩 Syntax:
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;

class Date {
private:
    int day;
    int month;
    int year;
public:
    Date() {}
    Date(int day, int month, int year) {
        this->day = day;
        this->month = month;
        this->year = year;
    }

    void set_date(const string& date_str) {
        // Extract day, month, and year from the date string
        int day = stoi(date_str.substr(0, 2));
        int month = stoi(date_str.substr(3, 2));
        int year = stoi(date_str.substr(6, 4));

        // Set the date
        this->day = day;
        this->month = month;
        this->year = year;

        cout << "date: " << day << "-" << month << "-" << year << endl;
    }


    int get_day() const {
        return day;
    }

    int get_month() const {
        return month;
    }

    int get_year() const {
        return year;
    }
};

class Bill {
public:
    Bill(double amt) : amount(amt), paid(false) {}
    virtual string get_type() const = 0;
    double get_amount() const { return amount; }
    bool is_paid() const { return paid; }
    void mark_paid() { paid = true; }

private:
    double amount;
    bool paid;
};

class WaterBill : public Bill {
public:
    WaterBill(double amt) : Bill(amt) {}
    string get_type() const override { return "Water Bill"; }
};

class ElectricityBill : public Bill {
public:
    ElectricityBill(double amt) : Bill(amt) {}
    string get_type() const override { return "Electricity Bill"; }
};

class Account {
public:
    Account() {}
    Account(int num, double bal) : account_num(num), balance(bal) {}
    virtual string get_account_type() const { return "hoi"; }
    virtual ~Account() {} // add virtual destructor

    int get_account_num() const { return account_num; }
    double get_balance() const { return balance; }
    void deposit(double new_balance) { balance += new_balance; }
    void withdraw(double new_balance) { balance -= new_balance; }


private:
    int account_num;
    double balance;

};

class CheckingAccount : public Account {
public:
    CheckingAccount() : Account() {}
    CheckingAccount(int num, double bal) : Account(num, bal) {}
    string get_account_type() const override { return "Checking Account"; }
};

class SavingsAccount : public Account {
public:
    SavingsAccount() : Account() {}
    SavingsAccount(int num, double bal) : Account(num, bal) {}
    string get_account_type() const override { return "Savings Account"; }
};
class CreditCard : public Account {
private:
    Account* linked_account;
public:
    CreditCard(int account_num, double bal, Account* linked_account)
        : Account(account_num, bal), linked_account(linked_account) {}
    string get_account_type() const override { return "Creditcard Account"; }
    Account* get_linked_account() const {
        return linked_account;
    }
    ~CreditCard() {

    }
    
};



class User {
public:
    User(string username, string password, string role) :
        username(username), password(password), role(role) {}

    string get_username() const {
        return username;
    }

    string get_password() const {
        return password;
    }

    string get_role() const {
        return role;
    }

    vector<shared_ptr<Account>>& get_accounts() {
        return accounts;
    }

    void add_account(shared_ptr<Account> account) {
        accounts.push_back(account);
    }

private:
    vector<shared_ptr<Account>> accounts;
    string username;
    string password;
    string role;
};



class Bank {
private:
    vector<CheckingAccount> checking_accounts;
    vector<SavingsAccount> savings_accounts;
    vector<CreditCard> creditcard_accounts;
    vector<User> users;
    User* current_user;
    string current_user_role;
    unordered_map<string, vector<Account*>> user_accounts;
    vector<Bill*> open_bills;
    unordered_map<string, vector<Bill*>> user_bills;
 

public:
    Bank() {
        // create some initial accounts
        checking_accounts.push_back(CheckingAccount(123, 1000.0));
        checking_accounts.push_back(CheckingAccount(444, 200.0));
        savings_accounts.push_back(SavingsAccount(456, 5000.0));
        checking_accounts.push_back(CheckingAccount(789, 2500.0));
        savings_accounts.push_back(SavingsAccount(321, 4000.0));  // added savings account for alice

        // initialize bills for henk and alice accounts
        user_bills["alice"].push_back(new WaterBill(75.0)); // bill for alice
        user_bills["alice"].push_back(new ElectricityBill(100.0)); // bill for alice
        user_bills["henk"].push_back(new WaterBill(50.0)); // bill for henk
        user_bills["henk"].push_back(new ElectricityBill(80.0)); // bill for henk

        // create some initial users
        users.push_back(User("alice", "pass1", "customer"));
        users.push_back(User("henk", "zomer", "customer"));
        users.push_back(User("bob", "pass2", "cashier"));
        users.push_back(User("charlie", "pass3", "director"));

        // add creditcard accounts for alice and henk
        CheckingAccount* alice_linked_acc = nullptr;
        CheckingAccount* henk_linked_acc = nullptr;
        for (auto& acc : checking_accounts) {
            if (acc.get_account_num() == 123) {
                alice_linked_acc = &acc;
            }
            else if (acc.get_account_num() == 789) {
                henk_linked_acc = &acc;
            }
        }

        creditcard_accounts.push_back(CreditCard(8888, -500.0, alice_linked_acc));
        creditcard_accounts.push_back(CreditCard(9999, -1000.0, henk_linked_acc));

        // populate the user_accounts map with the accounts of each user
        for (auto& user : users) {
            if (user.get_username() == "alice") {  // add alice's accounts
                user_accounts[user.get_username()].push_back(&checking_accounts[0]);
                user_accounts[user.get_username()].push_back(&savings_accounts[0]);
                user_accounts[user.get_username()].push_back(&creditcard_accounts[0]);
            }
            else if (user.get_username() == "henk") {  // add henk's accounts
                user_accounts[user.get_username()].push_back(&checking_accounts[1]);
                user_accounts[user.get_username()].push_back(&checking_accounts[2]);
                user_accounts[user.get_username()].push_back(&savings_accounts[1]);
                user_accounts[user.get_username()].push_back(&creditcard_accounts[1]);
            }
            else {
                for (auto& acc : checking_accounts) {
                    user_accounts[user.get_username()].push_back(&acc);
                }
                for (auto& acc : savings_accounts) {
                    user_accounts[user.get_username()].push_back(&acc);
                }
                
            }
        }
    }




    // function to find user by username and password
    User* find_user(string username, string password) {
        for (auto& user : users) {
            if (user.get_username() == username && user.get_password() == password) {
                return &user;
            }
        }
        return nullptr;
    }

    bool login(string username, string password) {
        User* user = find_user(username, password);
        if (user != nullptr) {
            current_user = user;
            current_user_role = user->get_role();
            return true;
        }
        else {
            return false;
        }
    }

        void logout() {
            current_user = nullptr;
            current_user_role = "";
        }

        bool is_logged_in() const {
            return current_user != nullptr;
        }

        string get_current_user_role() const {
            return current_user_role;
        }

        bool has_access_to_account(Account* account) const {
            if (!is_logged_in()) {
                return false;
            }

            // check if the account belongs to the current user
            for (auto acc : user_accounts.at(current_user->get_username())) {
                if (acc == account) {
                    return true;
                }
            }

            // check if the user has the required role to access the account
            if (current_user_role == "cashier") {
                return true;
            }
            else if (current_user_role == "director") {
                return true;
            }
            else {
                return false;
            }
        }


        // function to deposit money into an account
        void deposit(int account_num, double amount) {
            for (auto acc : user_accounts.at(current_user->get_username())) {
                if (acc->get_account_type() == "Creditcard Account") {
                    acc->deposit(amount-0.5);
                    cout << "deposit of " << amount << " made to credit card account " << account_num << endl;
                    return;
                    
                }
                else if (acc->get_account_num() == account_num) {
                    acc->deposit(amount);
                    cout << "deposit of " << amount << " made to credit card account " << account_num << endl;
                }
            }
        }

        // function to withdraw money from an account
        bool withdraw(int account_num, double amount) {
            for (auto acc : user_accounts.at(current_user->get_username())) {
                if (acc->get_account_type() == "Creditcard Account") {
                    acc->withdraw(amount + 0.5);
                    cout << "withdrawal of " << amount << " from credit card account " << account_num << endl;
                    return true;

                }

                else if (acc->get_account_num() == account_num) {
                    acc->withdraw(amount);
                    cout << "withdrawal of " << amount << " from credit card account " << account_num << endl;
                    return true;
                }
            }
            return false;
        }

        void view_balance(int account_num) {
            bool found = false;
            for (auto& acct : user_accounts[current_user->get_username()]) {
                if (acct->get_account_num() == account_num) {
                    cout << "Balance: $" << acct->get_balance() << endl;
                    found = true;
                    break;
                }
            }
            if (!found) {
                cout << "Account not found." << endl;
            }
        }

        void view_bills() {
            for (auto bill : open_bills) {
                cout << bill->get_type() << ": $" << bill->get_amount() << endl;
            }
        }

        void add_bill(Bill * bill) {
            open_bills.push_back(bill);
            cout << bill->get_type() << " added." << endl;
        }

        void pay_bill(Bill* bill, int account_num) {
            // find the account with the given account number
            Account* account = nullptr;
            for (auto& acc : user_accounts[current_user->get_username()]) {
                if (acc->get_account_num() == account_num) {
                    account = acc;
                    break;
                }
            }
            if (account == nullptr) {
                cout << "Account not found." << endl;
                return;
            }

            if (account->get_account_type() == "Creditcard Account") {
                bill->mark_paid();
                account->withdraw(-(bill->get_amount()+0.5));
            }
            else {


                // check if there are sufficient funds in the account
                if (bill->get_amount() > account->get_balance()) {
                    cout << "Insufficient funds." << endl;
                    return;
                }


                // mark the bill as paid and deduct the amount from the account
                bill->mark_paid();
                account->withdraw(bill->get_amount());
            }
        }
        vector<Account*> get_user_accounts() const {
            vector<Account*> accounts;
            if (!is_logged_in()) {
                return accounts;
            }

            // retrieve the accounts associated with the current user
            auto user_account_ptrs = user_accounts.at(current_user->get_username());
            for (auto account_ptr : user_account_ptrs) {
                accounts.push_back(account_ptr);
            }

            return accounts;
        }

        vector<Bill*> get_user_bills(string username) {
            return user_bills[username];
        }

        void add_interest(Date current_date) {
            if (current_date.get_month() == 12 && current_date.get_day() == 31) {
                for (CheckingAccount& checking_account : checking_accounts) {
                    double interest_rate = 0.01;
                    double interest = checking_account.get_balance() * interest_rate;
                    checking_account.deposit(interest); // double interest if it's 31st December
                }
                for (SavingsAccount& savings_account : savings_accounts) {
                    double interest_rate = 0.05;
                    double interest = savings_account.get_balance() * interest_rate;
                    savings_account.deposit(interest); // double interest if it's 31st December
                }
                cout << "Interest added to accounts" << endl;
            }
            else {
                cout << "No interest added today." << endl;
            }
        }

        void negative_monthly_interest(Date current_date) {
            if (current_date.get_day() == 28) {
                for (CheckingAccount& checking_account : checking_accounts) {
                    if (checking_account.get_balance() < 0) {
                        double interest_rate = 0.03;
                        double interest = checking_account.get_balance() * interest_rate;
                        checking_account.withdraw(interest); // Negative interest added to checking account, due to balance being negative
                        cout << "Negative interest added to checking accounts" << endl;
                    }

                }

            }

        }


        void deduce_creditcard_balance(const Date& current_date) {
            if (current_date.get_day() >= 28) {
                for (CreditCard& creditcard_account : creditcard_accounts) {
                    auto linked_acc = creditcard_account.get_linked_account();
                    double cc_balance = creditcard_account.get_balance();
                    if (cc_balance < 0) {
                        linked_acc->withdraw((-1 * cc_balance));
                    }
                    else { linked_acc->deposit((cc_balance)); }

                    if (cc_balance <= 0) {
                        creditcard_account.deposit(-1 * cc_balance);
                        
                    }
                    else {
                        creditcard_account.withdraw(cc_balance);
                        
                    }
                    cout << "creditcard balance is deducted from checking account." << endl;
                }
            }
        }

        User* get_user(const string& username) {
            for (auto& user : users) {
                if (user.get_username() == username) {
                    return &user;
                }
            }
            return nullptr;
        }

        bool add_account(const string& username) {
            User* user = get_user(username);
            if (user == nullptr) {
                cout << "Error: User not found." << endl;
                return false;
            }

            int choice;
            bool added = false;
            do {
                cout << endl << "Select account type to add: " << endl;
                cout << "1. Checking Account" << endl;
                cout << "2. Savings Account" << endl;
                cout << "3. Credit Card Account" << endl;
                cout << "4. Cancel" << endl;
                cout << "Choice: ";
                cin >> choice;
                cout << endl;

                switch (choice) {
                case 1: {
                    int account_num;
                    double balance;
                    cout << "Enter account number: ";
                    cin >> account_num;
                    cout << "Enter balance: ";
                    cin >> balance;
                    auto acc = make_shared<CheckingAccount>(account_num, balance);
                    user->add_account(acc);
                    cout << "Checking account added successfully." << endl;
                    added = true;
                    break;
                }
                case 2: {
                    int account_num;
                    double balance;
                    cout << "Enter account number: ";
                    cin >> account_num;
                    cout << "Enter balance: ";
                    cin >> balance;
                    auto acc = make_shared<SavingsAccount>(account_num, balance);
                    user->add_account(acc);
                    cout << "Savings account added successfully." << endl;
                    added = true;
                    break;
                }
                case 3: {
                    int account_num;
                    double balance;
                    int linked_account_num;
                    cout << "Enter account number: ";
                    cin >> account_num;
                    cout << "Enter balance: ";
                    cin >> balance;
                    cout << "Enter checking account number to link creditcard account to: ";
                    cin >> linked_account_num;
                    CreditCard* user_linked_acc = nullptr;
                    for (auto& acc : creditcard_accounts) {
                        if (acc.get_account_num() == linked_account_num) {
                            user_linked_acc = &acc;
                            break;
                        }
                    }
                    auto acc = make_shared<CreditCard>(account_num, balance, user_linked_acc);
                    if (user_linked_acc == nullptr) {
                        cout << "Error: Invalid linked account number. Credit card account not added." << endl;
                    }
                    else {
                        user->add_account(acc);
                        cout << "Credit card account added successfully." << endl;
                        added = true;
                    }
                    break;
                }
                case 4:
                    break;
                default:
                    cout << "Invalid choice. Please try again." << endl;
                    break;
                }
            } while (choice != 4 && !added);
            return added;
        }





        vector<Account*> get_user_accounts(const string& username) const {
            vector<Account*> accounts;
            auto it = user_accounts.find(username);
            if (it != user_accounts.end()) {
                for (auto account : it->second) {
                    accounts.push_back(account);
                }
            }
            return accounts;
        }

        void list_accounts(const string& username) {
            User* user = get_user(username);
            if (user == nullptr) {
                cout << "Error: User not found." << endl;
                return;
            }

            vector<shared_ptr<Account>> accounts;
            for (auto acc : user_accounts[username]) {
                if (auto ca = dynamic_cast<CheckingAccount*>(acc)) {
                    accounts.push_back(make_shared<CheckingAccount>(*ca));
                }
                else if (auto sa = dynamic_cast<SavingsAccount*>(acc)) {
                    accounts.push_back(make_shared<SavingsAccount>(*sa));
                }
                else if (auto cca = dynamic_cast<CreditCard*>(acc)) {
                    accounts.push_back(make_shared<CreditCard>(*cca));
                }
            }

            cout << "Number of accounts for user " << username << ": " << accounts.size() << endl;
            for (auto acc : accounts) {
                cout << "Account number for user " << username << ": " << acc->get_account_num() << endl;
            }

            cout << endl << "Accounts for user " << username << ":" << endl;
            for (auto acc : accounts) {
                if (auto ca = dynamic_pointer_cast<CheckingAccount>(acc)) {
                    cout << ca->get_account_type() << " (" << ca->get_account_num() << "): " << ca->get_balance() << endl;
                }
                else if (auto sa = dynamic_pointer_cast<SavingsAccount>(acc)) {
                    cout << sa->get_account_type() << " (" << sa->get_account_num() << "): " << sa->get_balance() << endl;
                }
                else if (auto cca = dynamic_pointer_cast<CreditCard>(acc)) {
                    cout << cca->get_account_type() << " (" << cca->get_account_num() << "): " << cca->get_balance() << endl;
                }
            }
        }




};
 


int main() {
    Bank bank;
    string username, password;
    Date date(1, 1, 2022); // creates a date object with values 1, 1, 2022

    while (true) {
        cout << "Welcome to the bank. Please login to continue." << endl;
        cout << "1. Login" << endl;
        cout << "2. set date" << endl;
        cout << "3. Quit" << endl;
        cout << "Enter choice (1-3: ";
        int choice;
        cin >> choice;
        if (choice == 3) {
            break;
        }
        else if (choice == 1) {
            cout << "Username: ";
            cin >> username;
            cout << "Password: ";
            cin >> password;
            if (bank.login(username, password)) {
                cout << "Login successful!" << endl;

                bank.add_interest(date);
                bank.deduce_creditcard_balance(date);
                bank.negative_monthly_interest(date);
                
            }
            else {
                cout << "Invalid username or password. Please try again." << endl;
                continue;
            }
        }

        else if (choice == 2) {
            cout << "date should be in format dd-mm-yyyy" << endl;
            cout << "set date: ";
            string input_date;
            cin >> input_date;
            date.set_date(input_date);
            continue;
            
        } 
        else {
            cout << "Invalid choice. Please try again." << endl;
            continue;
        }


        User* user = bank.get_user(username);
        if (user->get_role() == "customer") {
            // The user is a customer
            while (true) {
                cout << endl << "What would you like to do?" << endl;
                cout << "1. View bank accounts" << endl;
                cout << "2. View bills" << endl;
                cout << "3. Log out" << endl;
                cout << "Enter choice (1-3): ";
                cin >> choice;
                if (choice == 3) {
                    bank.logout();
                    break;
                }
                else if (choice == 2) {
                    cout << "Your bills:" << endl;
                    for (auto bill : bank.get_user_bills(username)) {
                        cout << bill->get_type() << " - $" << bill->get_amount() << endl;
                    }

                }
                else if (choice == 1) {
                    vector<Account*> accounts = bank.get_user_accounts();
                    cout << endl << "Your accounts:" << endl;
                    int i = 1;
                    map<int, Account*> account_map;
                    for (auto acc : accounts) {
                        cout << i << ". " << acc->get_account_type() << " (" << acc->get_account_num() << "): " << acc->get_balance() << endl;
                        account_map[i] = acc;
                        i++;
                    }
                    cout << i << ". Go back" << endl;
                    cout << "Select an account (1-" << i << "): ";
                    int account_choice;
                    cin >> account_choice;
                    if (account_choice == i) {
                        continue;
                    }
                    Account* account = account_map[account_choice];
                    cout << "Selected account: " << account->get_account_type() << " (" << account->get_account_num() << ")" << endl;
                    while (true) {
                        cout << endl << "What would you like to do?" << endl;
                        cout << "1. Deposit" << endl;
                        cout << "2. Withdraw" << endl;
                        cout << "3. Pay bill" << endl;
                        cout << "4. Go back to main menu" << endl;
                        cout << "Enter choice (1-5): ";
                        cin >> choice;
                        if (choice == 1) {
                            cout << "Enter amount to deposit: ";
                            double amount;
                            cin >> amount;
                            bank.deposit(account->get_account_num(), amount);
                            cout << "Deposit successful. New balance: " << account->get_balance() << endl;
                        }
                        else if (choice == 2) {
                            cout << "Enter amount to withdraw: ";
                            double amount;
                            cin >> amount;
                            if (bank.withdraw(account->get_account_num(), amount)) {
                                cout << "Withdrawal successful. New balance: " << account->get_balance() << endl;
                            }
                            else {
                                cout << "Insufficient funds. Withdrawal unsuccessful." << endl;
                            }
                        }
                        else if (choice == 3) {
                            cout << "Your bills:" << endl;
                            vector<Bill*> user_bills = bank.get_user_bills(username);
                            for (int i = 0; i < user_bills.size(); i++) {
                                cout << i + 1 << ". " << user_bills[i]->get_type() << " - $" << user_bills[i]->get_amount() << endl;
                            }
                            cout << "Select bill: ";
                            int bill_index;
                            cin >> bill_index;
                            if (bill_index <= 0 || bill_index > user_bills.size()) {
                                cout << "Invalid selection." << endl;
                                return 0;
                            }
                            Bill* bill = user_bills[bill_index - 1];
                            bank.pay_bill(bill, account->get_account_num());
                            cout << "Bill paid." << endl;
                        }

                        else if (choice == 4) {
                            break;
                        }
                        else {
                            cout << "Invalid choice. Please try again." << endl;
                        }
                    }
                }

            }
        }
        else if (user->get_role() == "cashier") {
            while (true) {
                cout << endl << "What would you like to do?" << endl;
                cout << "1. View all accounts of a user" << endl;
                cout << "2. Open a new account for a user" << endl;
                cout << "3. Log out" << endl;
                cout << "Enter choice (1-3): ";
                int choice;
                cin >> choice;
                if (choice == 3) {
                    bank.logout();
                    break;
                }
                else if (choice == 1) {
                    cout << "Enter username: ";
                    string username;
                    cin >> username;
                    vector<Account*> accounts = bank.get_user_accounts(username);
                    if (accounts.empty()) {
                        cout << "No accounts found for user " << username << endl;
                    }
                    else {
                        cout << endl << "Accounts for user " << username << ":" << endl;
                        for (auto acc : accounts) {
                            cout << acc->get_account_type() << " (" << acc->get_account_num() << "): " << acc->get_balance() << endl;
                        }
                    }

                }

                else if (choice == 2) {
                    cout << "Enter username: ";
                    string username;
                    cin >> username; 
                    if (bank.add_account(username)) {
                            cout << "Account successfully opened for user " << username << endl;   
                            bank.list_accounts(username);
                        }
                    else {
                            cout << "Failed to open account for user " << username << endl;
                        }
                    }
                }

            
        }


        else if (user->get_role() == "director") {
            // The user is a director
        }


        
    }
    return 0;
}