/* **++ ** ** MODULE DESCRIPTION: ** ** This is second project in ECE 3311. ** "Transaction DataBase" ** BANKDB ** ** ** ** AUTHOR: ** ** Nasser M. Abbasi, Network Northeastern Student ** ** CREATION DATE: 11/27/93 ** ** DESIGN ISSUES: ** ** This is based on Object Oriented Design approach using C++ ** ** INCLUDED FILES: ** ** iostream.h ** ** PORTABILITY ISSUES: ** ** VAX/VMS VAX C++ compiler, and UNIX (g++ compiler only) ** ** ** ASSUMPTIONS: ** ** The transaction input file must be sorted according to the ** Date field in ascending order. ** ** year dates are assumed to 19**, so any year is taken as befor ** the year 2000 . **-- */ /* ** ** INCLUDE FILES ** */ #include #define DEBUG 0 #define TRUE 1 #define FALSE 0 #define GOOD 1 #define BAD 0 #define MAX_LINE_LENGTH 256 #define MAX_NAME_LENGTH 10 #define MAX_NAME_FIELD_SIZE 10 #define DATE_FIELD_SIZE 8 #define ACCOUNT_NUMBER_FIELD_SIZE 6 #define TYPE_FIELD_SIZE 1 #define MAX_AMOUNT_FIELD_SIZE 10 #define ACTION_FIELD_SIZE 1 /* ** FUNCTIONS PROTOTYPING */ void debug(char *str); /* ** TYPE DEFINITIONS (no Data, all Data will be encapsulated inside the ** the objects they belong to ) */ typedef unsigned long int UINT; typedef unsigned short STATUS; typedef unsigned short BOOLEAN; enum TRAN_TYPE{SAVING = 1, CHECKING = 2}; enum ACTION_TYPE {DEPOSIT = 1, WITHDRAW = 2, TRANSFER = 3, OPEN = 4, BALANCE = 5}; enum ACCOUNT_STATUS{OPENED = 1, CLOSED = 2}; typedef struct { short MM; short DD; short YY; } DATE; typedef struct input_tran_record { DATE date; char last_name[MAX_NAME_FIELD_SIZE]; short last_name_size; char first_name[MAX_NAME_FIELD_SIZE]; short first_name_size; int account_number; enum TRAN_TYPE tran_type; float amount; enum ACTION_TYPE tran_action; } INPUT_TRAN_RECORD; struct account; // forward reference to the structure below // to enable self reference, in C++ this is needed typedef struct // the account type defintion { int account_number; char last_name[MAX_NAME_LENGTH + 1]; char first_name[MAX_NAME_LENGTH + 1]; struct { enum ACCOUNT_STATUS status; float saving_balance; DATE last_accessed; /* for interest calculations */ } saving_account; struct { enum ACCOUNT_STATUS status; float checking_balance; BOOLEAN is_went_below_once; BOOLEAN is_has_too_many_withdrawals_once; int number_of_withdrawals_so_far; } checking_account; struct account *next; }ACCOUNT; /********************************************************************\ * * I O M A N A G E R C L A S S * ********************************************************************/ class Io_Manager { private: FILE *trans_file; FILE *db_file; STATUS status; public: Io_Manager(); // Constructor ~Io_Manager(); // Destructor STATUS Get_Input_Line(char input_line[] ,UINT *input_line_size ); FILE *Get_Balance_File() { return(db_file); } }; /********************************************************************\ * * P A R S O R C L A S S * ********************************************************************/ class Parsor { private: // private buffers for parsor only usage unsigned short int index; char work_buf[MAX_LINE_LENGTH + 1]; char action[ACTION_FIELD_SIZE + 1]; char amount[MAX_AMOUNT_FIELD_SIZE + 1]; char type[TYPE_FIELD_SIZE + 1]; char account_number[ACCOUNT_NUMBER_FIELD_SIZE + 1]; char last_name[MAX_LINE_LENGTH + 1]; int last_name_size; int first_name_size; char first_name[MAX_LINE_LENGTH + 1]; char date[DATE_FIELD_SIZE + 1]; STATUS status; public: Parsor() {} ; ~Parsor() {} ; STATUS Parse_Input_Line (INPUT_TRAN_RECORD * input_tran_record , char *input_line); STATUS Parse_Date(char *, INPUT_TRAN_RECORD *); STATUS Parse_Name(char *, INPUT_TRAN_RECORD *); STATUS Parse_Account_Number(char *, INPUT_TRAN_RECORD *); STATUS Parse_Type(char *, INPUT_TRAN_RECORD *); STATUS Parse_Amount(char *, INPUT_TRAN_RECORD *); STATUS Parse_Action(char *, INPUT_TRAN_RECORD *); }; /********************************************************************\ * * D I S P A T C H E R C L A S S * ********************************************************************/ class Dispatcher { private: INPUT_TRAN_RECORD input_tran_record; STATUS status; public: Dispatcher() {}; ~Dispatcher() {}; STATUS Dispatch(char input_line[]); }; /********************************************************************\ * * A C C O U N T S D A T A B A S E (linked_list) * ********************************************************************/ class Accounts_Data_Base { private: int how_many_accounts_in_list; // number of entries in the list ACCOUNT *head_of_list; // head of singly linked list public: Accounts_Data_Base(); // Constructor ~Accounts_Data_Base(); // Destructor void Append_Entry(ACCOUNT *this_account); // add to end of linked list ACCOUNT *Get_The_Account(int account_number); // get from linked list STATUS Exisiting_Account(int account_number // see if an entry exist , enum TRAN_TYPE type); // on the linked list }; /********************************************************************\ * * A C C O U N T I N T E R E S T M A N A G E R C L A S S * ********************************************************************/ class Interest_Calculations_Manager { private: long int number_of_years; long int number_of_months; int i, j; float money ; float old_money ; public: Interest_Calculations_Manager() {money = 0.0; old_money= 0.0;} ~Interest_Calculations_Manager() {} void Interest_Calculations(ACCOUNT *, INPUT_TRAN_RECORD *); }; /********************************************************************\ * * A C C O U N T M A N A G E R C L A S S * ********************************************************************/ class Account_Manager : public Accounts_Data_Base // Inheret accounts_db { private: STATUS status; public: Account_Manager() {}; ~Account_Manager() {}; ACCOUNT * the_account; STATUS Validate_Input_Tran(INPUT_TRAN_RECORD * tran); STATUS Open_Tran(INPUT_TRAN_RECORD *input_tran); STATUS Deposit_Tran(INPUT_TRAN_RECORD *input_tran); STATUS Withdraw_Tran(INPUT_TRAN_RECORD *input_tran); STATUS Transfer_Tran(INPUT_TRAN_RECORD *input_tran); STATUS Balance_Tran(INPUT_TRAN_RECORD *input_tran); STATUS Open_New_Account(INPUT_TRAN_RECORD *input_tran); STATUS Open_Saving_Account(ACCOUNT * this_account, INPUT_TRAN_RECORD *input_tran); STATUS Open_Checking_Account(ACCOUNT * this_account, INPUT_TRAN_RECORD *input_tran); STATUS Withdraw_From_Checking(ACCOUNT *account, INPUT_TRAN_RECORD * tran); STATUS Withdraw_From_Saving(ACCOUNT *account, INPUT_TRAN_RECORD *tran); void Withdraw_Penalty(ACCOUNT * the_account , int less_500 , float amount_withdrawn); float Get_Balance(int, enum TRAN_TYPE); }; /*********************************************************************** ** ** O B J E C T S C R E A T I O N ** ** ***********************************************************************/ Io_Manager Io_Manager; // Create the Io_Manager Object Dispatcher Dispatcher; // Create the Dispatcher Object Parsor Parsor; // create the parsor Account_Manager Account_Manager; // create the account manager Accounts_Data_Base Accounts_Data_Base; // create db , linked list Interest_Calculations_Manager Interest_Calculations_Manager; // create interests manager /********************************************************************\ * * Io_Manager Constructor * ********************************************************************/ Io_Manager::Io_Manager() { trans_file = fopen("trans.act", "r"); if (trans_file == NULL) { cout << "OPEN_FILES: Error opening trans.act ...\n"; exit(1); } db_file = fopen("db.rec", "w"); if (db_file == NULL) { cout << "OPEN_FILES: Error opening db.rec ...\n" ; exit(1); } } /********************************************************************\ * * Io_Manager Destructor * ********************************************************************/ Io_Manager::~Io_Manager() { fclose(trans_file); fclose(db_file); } /********************************************************************\ * * Io_Manager::Get_Input_Line * ********************************************************************/ STATUS Io_Manager::Get_Input_Line (char input_line[] ,UINT *input_line_size ) { if (NULL == fgets(input_line, MAX_LINE_LENGTH , trans_file)) return(BAD); else { *input_line_size = strlen(input_line) - 1; /*to account for extra \n */ input_line[ *input_line_size] = '\0'; return(GOOD); } } /********************************************************************\ * * Dispatcher::Dispatch * ********************************************************************/ STATUS Dispatcher::Dispatch(char input_line[]) { // send message to parsor tp parse the input line into a transaction status = Parsor.Parse_Input_Line(&input_tran_record,input_line); if( status == BAD) { debug("failed to parse input line ...\n"); return(BAD); } status = Account_Manager.Validate_Input_Tran(&input_tran_record); if( status == BAD) { debug("failed to validate input line ...\n"); return(BAD); } // now we have a valid transaction fields, we process it // by sending message to the account manager switch (input_tran_record.tran_action) { case (OPEN) : status = Account_Manager.Open_Tran(&input_tran_record); break; case (DEPOSIT) : status = Account_Manager.Deposit_Tran(&input_tran_record); break; case (WITHDRAW): status = Account_Manager.Withdraw_Tran(&input_tran_record); break; case (TRANSFER): status = Account_Manager.Transfer_Tran(&input_tran_record); break; case (BALANCE): status = Account_Manager.Balance_Tran(&input_tran_record); break; } return(status); } /********************************************************************\ * * Accounts_Data_Base constructor * ********************************************************************/ Accounts_Data_Base::Accounts_Data_Base() { Accounts_Data_Base.how_many_accounts_in_list = 0; Accounts_Data_Base.head_of_list = NULL; } /********************************************************************\ * * Accounts_Data_Base Destructor * ********************************************************************/ Accounts_Data_Base::~Accounts_Data_Base() { ACCOUNT * temp = Accounts_Data_Base.head_of_list; ACCOUNT * next; while (temp != NULL) { next = (ACCOUNT *)(*temp).next; delete(temp); temp = next; } ; } /********************************************************************\ * * Accounts_Data_Base::Exisiting_Account * ********************************************************************/ STATUS Accounts_Data_Base::Exisiting_Account (int account_number , enum TRAN_TYPE type) { ACCOUNT * next; /* * if an account exist in the accounts database then return TRUE * else return FALSE */ next = Accounts_Data_Base.head_of_list; while (next != NULL) { if ((*next).account_number == account_number) if (type == SAVING) if ((*next).saving_account.status == OPENED) return(TRUE); else return(FALSE); else if ((*next).checking_account.status == OPENED) return(TRUE); else ; else ; next = (ACCOUNT *)(*next).next; } ; return(FALSE); } /********************************************************************\ * * Accounts_Data_Base::Append_Entry * ********************************************************************/ void Accounts_Data_Base::Append_Entry(ACCOUNT *this_account) { ACCOUNT *next; Accounts_Data_Base.how_many_accounts_in_list ++; if (head_of_list == NULL) { debug("Append_Entry: found next to be NULL\n"); head_of_list = this_account; } else { debug("Append_Entry: Not Null first entry, appending\n"); next = head_of_list; while ((*next).next != NULL) next = (ACCOUNT *) (*next).next; (*next).next = (struct account *)this_account; } } /********************************************************************\ * * Accounts_Data_Base::Get_The_Account * ********************************************************************/ ACCOUNT *Accounts_Data_Base::Get_The_Account(int account_number) { ACCOUNT *next; next = Accounts_Data_Base.head_of_list; while (next != NULL) if ((*next).account_number == account_number) return(next); else next = (ACCOUNT *) (*next).next; return(NULL); } /********************************************************************\ * * Parsor::Parse_Input_Line * ********************************************************************/ STATUS Parsor::Parse_Input_Line ( INPUT_TRAN_RECORD * input_tran_record // format transaction , char *input_line // from this line ) { Parsor.index = 0; status = Parsor.Parse_Date(input_line, input_tran_record); if (status == BAD) return(BAD); Parsor.index ++; /* skip over blank to next field */ status = Parsor.Parse_Name(input_line, input_tran_record); if (status == BAD) return(BAD); Parsor.index ++; /* skip over blank to next field */ status = Parsor.Parse_Account_Number(input_line , input_tran_record); if (status == BAD) return(BAD); Parsor.index ++; /* skip over blank to next field */ status = Parsor.Parse_Type(input_line , input_tran_record); if (status == BAD) return(BAD); Parsor.index ++; /* skip over blank to next field */ status = Parsor.Parse_Amount(input_line, input_tran_record); if (status == BAD) return(BAD); Parsor.index ++; /* skip over blank to next field */ status = Parsor.Parse_Action(input_line, input_tran_record); return(status); } /********************************************************************\ * * Parsor::Parse_Date * ********************************************************************/ STATUS Parsor::Parse_Date (char input_line[] , INPUT_TRAN_RECORD *input_tran_record ) { int i, field_size; for (Parsor.index = 0, i = 0, field_size = 0 ; input_line[Parsor.index] != ' ' && field_size <= DATE_FIELD_SIZE - 1 ; i ++, Parsor.index ++, field_size ++) { date[i] = input_line[Parsor.index]; if ((Parsor.index == 2 || Parsor.index == 5) && input_line[Parsor.index] != '/') { debug("Date field must contain / between dates....\n"); return(BAD); } } if (field_size != DATE_FIELD_SIZE) { debug("Date field size is invalid, too short ....\n"); return(BAD); } else { memmove(&work_buf[0], &date[0], 2); work_buf[2] = '\0'; (*input_tran_record).date.MM = atoi(work_buf); memmove(&work_buf[0], &date[3], 2); work_buf[2] = '\0'; (*input_tran_record).date.DD = atoi(work_buf); memmove(&work_buf[0], &date[6], 2); work_buf[2] = '\0'; (*input_tran_record).date.YY = atoi(work_buf); } return(GOOD); } /********************************************************************\ * * Parsor::Parse_Name * ********************************************************************/ STATUS Parsor::Parse_Name (char input_line[] , INPUT_TRAN_RECORD *input_tran_record ) { int i, field_size, temp; for (field_size = 0, i = 0 ; input_line[ Parsor.index] != ' ' && input_line[ Parsor.index] != ',' && field_size <= MAX_NAME_FIELD_SIZE ; Parsor.index ++, field_size ++) { last_name[i] = input_line[ Parsor.index]; i ++; } last_name_size = field_size; memmove(&(*input_tran_record).last_name[0] , &last_name[0] , last_name_size ); (*input_tran_record).last_name_size = last_name_size; temp = field_size; if (field_size <= MAX_NAME_FIELD_SIZE) { if (input_line[ Parsor.index] == ',') { Parsor.index ++; /* skip over ',' */ field_size ++; for (i = 0 ; input_line[ Parsor.index] != ' ' && field_size <= MAX_NAME_FIELD_SIZE ; i ++, Parsor.index ++, field_size ++) first_name[i] = input_line[ Parsor.index]; if (input_line[ Parsor.index] != ' ') first_name_size = field_size - 2 - temp; else first_name_size = field_size - 1 - temp; memmove(&(*input_tran_record).first_name[0] , &first_name[0] , first_name_size ); (*input_tran_record).first_name_size = first_name_size; if (field_size > MAX_NAME_FIELD_SIZE && input_line[Parsor.index] != ' ') while (input_line[ Parsor.index] != ' ') Parsor.index ++; /* skip the rest of the name */ } else { first_name_size = 0; (*input_tran_record).first_name_size = first_name_size; while (input_line[ Parsor.index] != ' ') Parsor.index ++; /* skip the rest of the name */ } } else { first_name_size = 0; (*input_tran_record).first_name_size = first_name_size; while (input_line[ Parsor.index] != ' ') Parsor.index ++; /* skip the rest of the name */ } return(GOOD); } /********************************************************************\ * * Parsor::Parse_Account_Number * ********************************************************************/ STATUS Parsor::Parse_Account_Number (char input_line[] , INPUT_TRAN_RECORD *input_tran_record ) { int i, field_size; for (field_size = 0, i = 0 ; input_line[Parsor.index] != ' ' ; i ++, Parsor.index ++, field_size ++) account_number[i] = input_line[Parsor.index]; if (field_size != ACCOUNT_NUMBER_FIELD_SIZE) { debug("PARSE_INPUT_LINE: Account_number field size is invalid\n"); return(BAD); } memmove(&work_buf[0] , &account_number[0] , ACCOUNT_NUMBER_FIELD_SIZE ); work_buf[ACCOUNT_NUMBER_FIELD_SIZE] = '\0'; (*input_tran_record).account_number = atoi(work_buf); return(GOOD); } /********************************************************************\ * * Parsor::Parse_Type * ********************************************************************/ STATUS Parsor::Parse_Type (char input_line[] , INPUT_TRAN_RECORD *input_tran_record ) { int i, field_size; for (field_size = 0, i = 0 ; input_line[Parsor.index] != ' ' ; i ++, Parsor.index ++, field_size ++) type[i] = input_line[Parsor.index]; if (field_size != TYPE_FIELD_SIZE) { debug("PARSE_INPUT_LINE: TYPE field size is invalid\n"); return(BAD); } memmove(&work_buf[0], &type[0], TYPE_FIELD_SIZE); work_buf[TYPE_FIELD_SIZE] = '\0'; (*input_tran_record).tran_type = (enum TRAN_TYPE) atoi(work_buf); return(GOOD); } /********************************************************************\ * * Parsor::Parse_Amount * ********************************************************************/ STATUS Parsor::Parse_Amount (char input_line[] , INPUT_TRAN_RECORD *input_tran_record ) { int i, field_size; for (field_size = 0, i = 0 ; input_line[Parsor.index] != ' ' ; i ++, Parsor.index ++, field_size ++) amount[i] = input_line[Parsor.index]; if (field_size > MAX_AMOUNT_FIELD_SIZE) { debug("PARSE_INPUT_LINE: AMOUNT field size is invalid\n"); return(BAD); } memmove(&work_buf[0], &amount[0], field_size); work_buf[field_size] = '\0'; sscanf(&work_buf[0], "%f", &(*input_tran_record).amount); return(GOOD); } /********************************************************************\ * * Parsor::Parse_Action * ********************************************************************/ STATUS Parsor::Parse_Action (char input_line[] , INPUT_TRAN_RECORD *input_tran_record ) { int i, field_size; for (field_size = 0, i = 0 ; input_line[Parsor.index] != ' ' && input_line[Parsor.index] != '\n' && input_line[Parsor.index] != '\0' ; i ++, Parsor.index ++, field_size ++) action[i] = input_line[Parsor.index]; if (field_size != ACTION_FIELD_SIZE) { debug("PARSE_INPUT_LINE: ACTION field size is invalid\n"); return(BAD); } memmove(&work_buf[0], &action[0], ACTION_FIELD_SIZE); work_buf[ACTION_FIELD_SIZE] = '\0'; (*input_tran_record).tran_action = (enum ACTION_TYPE) atoi(work_buf); return(GOOD); } /********************************************************************\ * * Account_Manager::Get_Balance * ********************************************************************/ float Account_Manager::Get_Balance(int account_number , enum TRAN_TYPE tran_type) { ACCOUNT * account; account = Get_The_Account(account_number); if (tran_type == CHECKING) return((*account).checking_account.checking_balance); else return((*account).saving_account.saving_balance); } /********************************************************************\ * * Account_Manager::Validate_Input_Line * ********************************************************************/ STATUS Account_Manager::Validate_Input_Tran (INPUT_TRAN_RECORD * tran) { if (((*tran).date.DD < 1) || ((*tran).date.DD > 31)) { debug("Invalid day of month in date field detected....\n"); return(BAD); } if (((*tran).date.MM < 1) || ((*tran).date.MM > 12)) { debug("Invalid month in date field detected ...\n"); return(BAD); } if (((*tran).date.YY < 00) || ((*tran).date.YY > 99)) { debug("Invalid day of month in date field detected ...\n"); return(BAD); } if (((*tran).account_number < 111111) || ((*tran).account_number > 999999)) { debug("Invalid account number field detected ...\n"); return(BAD); } if (((*tran).tran_type != SAVING) && ((*tran).tran_type != CHECKING)) { debug("Invalid transaction type field detected ...\n"); return(BAD); } if (((*tran).amount < 0.00) || ((*tran).amount > 1000000.00)) { debug("Invalid amount field detected ...\n"); return(BAD); } if (((*tran).amount < 0.00) || ((*tran).amount > 1000000.00)) { debug("Invalid amount field detected ...\n"); return(BAD); } if (((*tran).tran_action != OPEN) && ((*tran).tran_action != DEPOSIT) && ((*tran).tran_action != WITHDRAW) && ((*tran).tran_action != BALANCE) && ((*tran).tran_action != TRANSFER)) { debug("Invalid transaction action field detected ...\n"); return(BAD); } return(GOOD); } /********************************************************************\ * * Account_Manager::Open_Tran * ********************************************************************/ STATUS Account_Manager::Open_Tran (INPUT_TRAN_RECORD * tran) { ACCOUNT * this_account; enum ACCOUNT_STATUS account_status = OPENED; if (((*tran).amount < 9.99) && ((*tran).tran_type == CHECKING)) { debug("PROCESS_OPEN_TRAN: Checking account amount too small to open\n"); return(BAD); } this_account = Accounts_Data_Base.Get_The_Account((*tran).account_number); if (this_account != NULL) { /* * account already exist, see if the specific part of it * exist, i.e if the incoming tran is SAVING, see if SAVING * has already been opened, if so reject the tran else open * that part */ switch ( (*tran).tran_type) { case (SAVING): if ((*this_account).saving_account.status == OPENED) { debug("PROCESS_OPEN: Saving Account exist, cant open \n"); return(BAD); } debug("Opening the Saving part for an existing account...\n"); status = Account_Manager.Open_Saving_Account(this_account, tran); return(status); break; case (CHECKING): if ((*this_account).checking_account.status == OPENED) { debug("PROCESS_OPEN: Checking Account exist, cant open \n"); return(BAD); } debug("Opening the checking part for an existing account...\n"); status = Account_Manager.Open_Checking_Account(this_account, tran); return(status); break; } } else { status = Account_Manager.Open_New_Account(tran); return(status); } } /********************************************************************\ * * Account_Manager::Open_New_Account * ********************************************************************/ STATUS Account_Manager::Open_New_Account(INPUT_TRAN_RECORD * tran) { ACCOUNT * next; ACCOUNT * this_account; enum ACCOUNT_STATUS account_status = OPENED; this_account = new ACCOUNT; if (this == NULL) { debug("PROCESS_OPEN: Failed to allocate memory for an account \n"); return(BAD); } (*this_account).next = NULL; (*this_account).account_number = (*tran).account_number; memmove(&(*this_account).last_name[0], &(*tran).last_name[0] , (*tran).last_name_size); (*this_account).last_name[(*tran).last_name_size] = '\0'; memmove(&(*this_account).first_name[0], &(*tran).first_name[0] , (*tran).first_name_size); (*this_account).first_name[(*tran).first_name_size] = '\0'; if ((*tran).tran_type == CHECKING) { debug("Creating a new account and opening the checking part for it...\n"); status = Account_Manager.Open_Checking_Account(this_account, tran); /* initialize the other part of the account to close */ account_status = CLOSED; (*this_account).saving_account.status = account_status; } else { debug("Creating a new account and opening the saving part for it...\n"); status = Account_Manager.Open_Saving_Account(this_account, tran); /* initialize the other part of the account to close */ account_status = CLOSED; (*this_account).checking_account.status = account_status; } /* * the account entry has been created and filled with the data * from the transaction. now go over the linked list (accounts_db) * and add it at the end of the linked list */ Accounts_Data_Base.Append_Entry(this_account); return(GOOD); } /********************************************************************\ * * Account_Manager::Open_Saving_Account * ********************************************************************/ STATUS Account_Manager::Open_Saving_Account(ACCOUNT * this_account , INPUT_TRAN_RECORD * tran ) { enum ACCOUNT_STATUS account_status; (*this_account).saving_account.saving_balance = (*tran).amount; (*this_account).saving_account.last_accessed = (*tran).date; account_status = OPENED; (*this_account).saving_account.status = account_status; return(GOOD); } /********************************************************************\ * * Account_Manager::Open_Checking_Account * ********************************************************************/ STATUS Account_Manager::Open_Checking_Account (ACCOUNT * this_account , INPUT_TRAN_RECORD * tran ) { ACCOUNT * next; enum ACCOUNT_STATUS account_status = OPENED; /* * here I check for below 500, we already know this sum cant be * less that 9.99, as this has already be validated early on * in the validate_input_tran function */ if ((*tran).amount < 500.00) { (*tran).amount = (*tran).amount - 10; (*this_account).checking_account.is_went_below_once = TRUE; } else (*this_account).checking_account.is_went_below_once = FALSE; (*this_account).checking_account.checking_balance = (*tran).amount; (*this_account).checking_account.number_of_withdrawals_so_far = 0; (*this_account).checking_account.is_has_too_many_withdrawals_once = FALSE; account_status = OPENED; (*this_account).checking_account.status = account_status; return(GOOD); } /********************************************************************\ * * Account_Manager::Deposit_Tran * ********************************************************************/ STATUS Account_Manager::Deposit_Tran (INPUT_TRAN_RECORD * tran) { enum TRAN_TYPE tran_type; enum ACTION_TYPE action; if (! Accounts_Data_Base.Exisiting_Account ((*tran).account_number, (*tran).tran_type)) { debug("Account Does not exist, Please Open first ....\n"); return(BAD); } the_account = Accounts_Data_Base.Get_The_Account((*tran).account_number); if ((*tran).tran_type == SAVING) { debug("Depositing into Saving ...\n"); /* first see if interest is due */ Interest_Calculations_Manager.Interest_Calculations(the_account, tran); (*the_account).saving_account.saving_balance = (*the_account).saving_account.saving_balance + (*tran).amount; if (DEBUG) printf("...the Balance now in SAVING after depositing = %10.2f ...\n" , (*the_account).saving_account.saving_balance ); } else { debug("Depositing into checking ...\n"); (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + (*tran).amount; if (DEBUG) printf("...the Balance now in checking account is = %10.2f ...\n", (*the_account).checking_account.checking_balance ); } return (GOOD); } /********************************************************************\ * * Account_Manager::Withdraw_Tran * ********************************************************************/ STATUS Account_Manager::Withdraw_Tran (INPUT_TRAN_RECORD * tran) { if (! Accounts_Data_Base.Exisiting_Account ((*tran).account_number, (*tran).tran_type)) { debug("PROCESS_Withdraw_TRAN: Account Does not exist, Open first \n"); return(BAD); } the_account = Accounts_Data_Base.Get_The_Account((*tran).account_number); if ((*tran).tran_type == SAVING) return(Account_Manager.Withdraw_From_Saving(the_account, tran)); else return(Account_Manager.Withdraw_From_Checking(the_account, tran)); } /********************************************************************\ * * Account_Manager::Withdraw_From_Checking * ********************************************************************/ STATUS Account_Manager::Withdraw_From_Checking(ACCOUNT * the_account , INPUT_TRAN_RECORD * tran) { enum TRAN_TYPE tran_type; enum ACTION_TYPE action; int less_500 = FALSE; debug("Withdrawing from checking ...\n"); if ((*the_account).checking_account.checking_balance <= (*tran).amount) { if (DEBUG) { printf("...Insufficient funds in checking to withdraw $%10.2f ..\n", (*tran).amount ); printf("...checking balance currently = $%10.2f \n", (*the_account).checking_account.checking_balance ); } return(BAD); } (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance - (*tran).amount; /* handle the case of less than 500 */ if ((*the_account).checking_account.checking_balance < 500.00) if ((*the_account).checking_account.is_went_below_once != TRUE) { if ((*the_account).checking_account.checking_balance < 9.99) { debug("Amount in checking is too small to withdraw from ...\n"); debug("less than $9.99 in account for penalty use ....\n"); /* Roll back, and reject this transaction */ debug("Rolling back the checking account...\n"); (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + (*tran).amount; return(BAD); } (*the_account).checking_account.is_went_below_once = TRUE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance - 10.00; less_500 = TRUE; } else ; else ; /* now check for number of withdraws penalty */ Account_Manager.Withdraw_Penalty (the_account, less_500,(*tran).amount); if (DEBUG) printf("...the Balance now in checking account is = %10.2f ...\n", (*the_account).checking_account.checking_balance ); return(GOOD); } /********************************************************************\ * * Account_Manager::Withdraw_Penalty * ********************************************************************/ void Account_Manager::Withdraw_Penalty(ACCOUNT * the_account , int less_500 , float amount_withdrawn) { if ((*the_account).checking_account.is_has_too_many_withdrawals_once != TRUE) { (*the_account).checking_account.number_of_withdrawals_so_far ++; if ((*the_account).checking_account.number_of_withdrawals_so_far >= 5) { (*the_account).checking_account.is_has_too_many_withdrawals_once= TRUE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance - 15.00; /* if this made the account balance go less than 0 , roll back */ if ((*the_account).checking_account.checking_balance < 0.00) { (*the_account).checking_account.number_of_withdrawals_so_far --; (*the_account).checking_account.is_has_too_many_withdrawals_once = FALSE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + 15.00; debug("WITHDRAW: 5th withdraw but insufficient funds"); debug(" to remove the $15. transaction Rejected...\n"); /* make sure to roll back the orginal amount allready withdrawn */ (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + amount_withdrawn; /* make sure to roll back the penalty on going below 500 also */ /* it could be that going below 500 coincided with the 5th */ /* withdraw attempt */ if (less_500 == TRUE) { (*the_account).checking_account.is_went_below_once = FALSE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + 10.00; } else ; } else ; } else ; } } /********************************************************************\ * * Account_Manager::Withdraw_From_Saving * ********************************************************************/ STATUS Account_Manager::Withdraw_From_Saving(ACCOUNT * the_account , INPUT_TRAN_RECORD * tran ) { debug("Withdrawing from Saving ...\n"); if ((*the_account).saving_account.saving_balance <= (*tran).amount) { if (DEBUG) { printf("...Insufficient funds in Saving to withdraw $%10.2f from..\n", (*tran).amount ); printf("...Saving balance currently = $%10.2f \n", (*the_account).saving_account.saving_balance ); } return(BAD); } /* All is ok, we have enough money in saving to withdraw */ Interest_Calculations_Manager.Interest_Calculations(the_account, tran); (*the_account).saving_account.saving_balance = (*the_account).saving_account.saving_balance - (*tran).amount; if (DEBUG) printf("...the Balance now in SAVING after depositing = %10.2f ...\n", (*the_account).saving_account.saving_balance ); return(GOOD); } /********************************************************************\ * * Account_Manager::Transfer_Tran * ********************************************************************/ STATUS Account_Manager::Transfer_Tran (INPUT_TRAN_RECORD * tran) { enum TRAN_TYPE tran_type; float balance; STATUS status; /* first check that the account exist */ if (! Accounts_Data_Base.Exisiting_Account ((*tran).account_number, (*tran).tran_type)) { debug("Account Does not exist, Open first ........\n"); return(BAD); } debug("Processing a transfer transaction....\n"); /* * now check the account we are transferring to exist. it could be that * only the checking part of the account is open or vis versa. * in transfer we need both accounts to be opened first else we reject * the transaction */ if ((*tran).tran_type == SAVING) { tran_type = CHECKING; if (! Accounts_Data_Base.Exisiting_Account ((*tran).account_number, tran_type)) { if (DEBUG) printf("Checking Account %d to transfer to must be Opened first... \n" , (*tran).account_number ); return(BAD); } } else { tran_type = SAVING; if (! Accounts_Data_Base.Exisiting_Account ((*tran).account_number, tran_type)) { if (DEBUG) printf("Saving Account %d to transfer to must be opened first ..\n" , (*tran).account_number ); return(BAD); } } /* * so far so good. both saving and checking accounts are open * now, make sure sufficient funds exist */ balance = Account_Manager.Get_Balance((*tran).account_number, (*tran).tran_type); if ((*tran).tran_type == SAVING) if ((*tran).amount > balance) { debug("not sufficient funds in saving account to transfer from...\n"); return(BAD); } else ; else if ((*tran).amount > balance) { debug("not sufficient funds in checking account to transfer from ...\n"); return(BAD); } else ; /* * so far so good, there is enough money to take from one account * and move to the other. go ahead and do the transfer. */ status = Account_Manager.Withdraw_Tran(tran); if (status == BAD) return(BAD); /* * now switch the transaction type to the other so we can credit that * account */ if ((*tran).tran_type == SAVING) tran_type = CHECKING; else tran_type = SAVING; (*tran).tran_type = tran_type; status = Account_Manager.Deposit_Tran(tran); if (status == BAD) return(BAD); return(GOOD); } /********************************************************************\ * * Interest_Calculations_Manager::Interest_Calculations * ********************************************************************/ void Interest_Calculations_Manager::Interest_Calculations (ACCOUNT * account, INPUT_TRAN_RECORD * tran) { number_of_years = (*tran).date.YY - (*account).saving_account.last_accessed.YY; if (number_of_years < 0) { debug("FATAL: Transaction in trans.act is not in chronological order\n"); debug("FATAL: Please SOrt input transaction in correct DATE order\n"); exit(1); } number_of_months = (*tran).date.MM - (*account).saving_account.last_accessed.MM; if (number_of_years == 0 && number_of_months < 0) { debug("FATAL: Transaction in trans.act is not in chronological order\n"); debug("FATAL: Please SOrt input transaction in correct DATE order\n"); exit(1); } if( number_of_years != 0) number_of_months = number_of_months + (number_of_years * 12); money = (*account).saving_account.saving_balance; old_money = money; /* * calculate compound interest for the number of months since * account accessed */ for (i = 0; i < number_of_months; i ++) money = money + (money * 0.005); /* half percent for each month */ (*account).saving_account.saving_balance = money; if (DEBUG) { if (money - old_money > 0.0) { printf("...Interest Credit is due to saving account....\n"); printf("Crediting the saving account interest for %d months\n", number_of_months); printf("Balance before= %10.2f, Balance after interest paid = %10.2f\n" , old_money, money); printf("Interest amount credited to account = %10.2f \n", (money - old_money)); } else { printf("No Interest Credit is due to saving account at this time..\n"); printf("Saving account balance is : %10.2f ...\n", old_money); } } /* * now, we make sure we UPDATE the date we last used the SAVING * account, this way next time we access this account, we will * pay the correct interest for the correct time period */ (*account).saving_account.last_accessed = (*tran).date; } /********************************************************************\ * * Account_Manager::Balance_Tran * ********************************************************************/ STATUS Account_Manager::Balance_Tran (INPUT_TRAN_RECORD * tran) { float balance; char work_buf[80]; the_account = Accounts_Data_Base.Get_The_Account((*tran).account_number); if( the_account == NULL) { debug("No such account found in Balance tran...\n"); return (BAD); } if ((*tran).tran_type == SAVING) Interest_Calculations_Manager.Interest_Calculations(the_account, tran); switch ( (*tran).tran_type) { case (SAVING): debug("Processing a BALANCE for a Saving account ...\n"); balance = (*the_account).saving_account.saving_balance; break; case (CHECKING): debug("Processing a BALANCE for a Checking account ...\n"); balance = (*the_account).checking_account.checking_balance; break; } /* now print the balance record to the file */ fprintf(Io_Manager.Get_Balance_File() , "%2d/%2d/%2d " , (*tran).date.MM, (*tran).date.DD, (*tran).date.YY ); memmove(work_buf , &(*tran).last_name , (*tran).last_name_size ); work_buf[(*tran).last_name_size] = '\0'; fprintf(Io_Manager.Get_Balance_File(), "%s", work_buf); if ((*tran).last_name_size < MAX_NAME_LENGTH) { work_buf[0] = ','; memmove(&work_buf[1] , &(*tran).first_name , (*tran).first_name_size ); work_buf[(*tran).first_name_size + 1] = '\0'; fprintf(Io_Manager.Get_Balance_File(), "%s", work_buf); } fprintf(Io_Manager.Get_Balance_File(), " %6d", (*tran).account_number); fprintf(Io_Manager.Get_Balance_File(), " %1d", (*tran).tran_type); fprintf(Io_Manager.Get_Balance_File(), " %10.2f", balance); fprintf(Io_Manager.Get_Balance_File(), " %1d\n", (*tran).tran_action); return(GOOD); } /********************************************************************\ * * M A I N * ********************************************************************/ main() { char input_line[MAX_LINE_LENGTH]; STATUS status; UINT input_line_size; while(TRUE) { // send message to Io_Manager object to get an input line status = Io_Manager.Get_Input_Line(input_line,&input_line_size); if( status == BAD || input_line_size == 0) { debug("MAIN: completed work on trans.act...\n"); return(1); } debug("----------------------------------------------------------\n"); debug(input_line); debug("\n"); // send message to Dispatcher to dispatch the transaction status = Dispatcher.Dispatch(input_line); if( status == BAD) debug("... Transaction rejected ...\n"); } } /********************************************************************\ * DEBUG ********************************************************************/ void debug(char *str) { if (DEBUG) cout << str; }