diff --git a/DebuggingAndRefactoringTask1.csproj b/DebuggingAndRefactoringTask1.csproj index 2150e37..c4ade07 100644 --- a/DebuggingAndRefactoringTask1.csproj +++ b/DebuggingAndRefactoringTask1.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/DebuggingAndRefactoringTask1.sln b/DebuggingAndRefactoringTask1.sln index 3ee88c1..471ee2b 100644 --- a/DebuggingAndRefactoringTask1.sln +++ b/DebuggingAndRefactoringTask1.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebuggingAndRefactoringTask1", "DebuggingAndRefactoringTask1.csproj", "{11171346-B978-47D5-9AF1-D90BEE023054}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebuggingAndRefactoringTask1", "DebuggingAndRefactoringTask1.csproj", "{11171346-B978-47D5-9AF1-D90BEE023054}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "..\UnitTests\UnitTests.csproj", "{07BCDB46-99BB-46D5-9844-BEBB68D6B844}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {11171346-B978-47D5-9AF1-D90BEE023054}.Debug|Any CPU.Build.0 = Debug|Any CPU {11171346-B978-47D5-9AF1-D90BEE023054}.Release|Any CPU.ActiveCfg = Release|Any CPU {11171346-B978-47D5-9AF1-D90BEE023054}.Release|Any CPU.Build.0 = Release|Any CPU + {07BCDB46-99BB-46D5-9844-BEBB68D6B844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07BCDB46-99BB-46D5-9844-BEBB68D6B844}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07BCDB46-99BB-46D5-9844-BEBB68D6B844}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07BCDB46-99BB-46D5-9844-BEBB68D6B844}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Enums/Enums.cs b/Enums/Enums.cs new file mode 100644 index 0000000..3dab625 --- /dev/null +++ b/Enums/Enums.cs @@ -0,0 +1,25 @@ +namespace DebuggingAndRefactoringTask1.Enums +{ + public static class Enums + { + //MessageType for Message Bus + public enum MessageType + { + Information = 0, + Success = 1, + Warning = 2, + Error = 3, + MenuItem = 4, + Input = 5 + + } + + //AccountInteractionType for Monetary interactions. + public enum AccountInteractionType + { + Deposit = 0, + Withdraw = 1, + Tranfer = 2 + } + } +} diff --git a/Models/Account.cs b/Models/Account.cs new file mode 100644 index 0000000..4a8cb0a --- /dev/null +++ b/Models/Account.cs @@ -0,0 +1,10 @@ +namespace DebuggingAndRefactoringTask1.Models +{ + public class Account + { + public int Id { get; set; } + public int AccountCode { get; set; } + public string AccountName { get; set; } + public double AccountBalance { get; set; } + } +} diff --git a/Models/AccountTransaction.cs b/Models/AccountTransaction.cs new file mode 100644 index 0000000..f06e24c --- /dev/null +++ b/Models/AccountTransaction.cs @@ -0,0 +1,14 @@ +using static DebuggingAndRefactoringTask1.Enums.Enums; + +namespace DebuggingAndRefactoringTask1.Models +{ + public class AccountTransaction + { + public int Id { get; set; } + public int AccountCodeTo { get; set; } + public int AccountCode { get; set; } + public double Amount { get; set; } + public DateTime TransactionDateTime { get; set; } + public AccountInteractionType TranactionType { get; set; } + } +} diff --git a/Program.cs b/Program.cs index 70ad718..577dad8 100644 --- a/Program.cs +++ b/Program.cs @@ -1,301 +1,306 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using DebuggingAndRefactoringTask1.ServiceLayer; +using static DebuggingAndRefactoringTask1.Enums.Enums; namespace BankingSystem { - class Program - { - enum MessageType - { - Information = 0, - Success =1, - Warning = 2, - Error = 3, - MenuItem = 4, - Input = 5 - - } - enum InputType - { - Numeric = 0, - Textual = 1, - } - enum AccountInteractionType - { - Deposit = 0, - Withdraw = 1 - } - - static List accounts = new List(); - + + public static class Program + { static void Main(string[] args) { + var accountServices = new AccountServices(); + var generalServices = new GeneralServices(); + var transactionServices = new TransactionServices(); + while (true) { - DisplayMessage(MessageType.Information, $"Options:\n"); - DisplayMessage(MessageType.MenuItem, $"1. Add Account"); - DisplayMessage(MessageType.MenuItem, $"2. Deposit Money"); - DisplayMessage(MessageType.MenuItem, $"3. Withdraw Money"); - DisplayMessage(MessageType.MenuItem, $"4. Display Account Details"); - DisplayMessage(MessageType.MenuItem, $"5. Exit \n"); - - var choiceParsed = GatherNumericInput($"Choice: "); - - switch (choiceParsed) - { - case 1: - AddAccount(); - break; - case 2: - MonetaryInteraction(AccountInteractionType.Deposit); - break; - case 3: - MonetaryInteraction(AccountInteractionType.Withdraw); - break; - case 4: - DisplayAccountDetails(); - break; - case 5: - break; - default: - break; - } - + RunApplication(accountServices, generalServices, transactionServices); } } - - static void AddAccount() + public static void RunApplication(AccountServices accountServices, GeneralServices generalServices, TransactionServices transactionServices) { - var accountCodeParsed = GatherNumericInput("Enter Account Number: "); - - if (accountCodeParsed != null) - { - var name = GatherTextualInput("Enter Account Holder Name: "); - - if (name != null) - { - Account account = new Account { Id = accounts.Count + 1, AccountCode = accountCodeParsed.Value, AccountName = name.Trim(), AccountBalance = 0 }; - accounts.Add(account); - - DisplayMessage(MessageType.Success, "Account added successfully."); - } - } - else + var choiceParsed = generalServices.DisplayMenu(); + + //Option Bus + switch (choiceParsed) { - DisplayMessage(MessageType.Error, "Account not added - Please try again"); - } + case 1: + AddAccount(accountServices, generalServices); + break; + case 2: + MonetaryInteraction(AccountInteractionType.Deposit, accountServices, generalServices, transactionServices); + break; + case 3: + MonetaryInteraction(AccountInteractionType.Withdraw, accountServices, generalServices, transactionServices); + break; + case 4: + DisplayAccountDetails(accountServices, generalServices); + break; + case 5: + DisplayAccountTransactionHistory(accountServices, generalServices, transactionServices); + break; + case 6: + MonetaryInteraction(AccountInteractionType.Tranfer, accountServices, generalServices, transactionServices); + break; + case 7: + EditAccountName(accountServices, generalServices, transactionServices); + break; + case 8: + DeleteAccount(accountServices, generalServices, transactionServices); + break; + case 9: + Environment.Exit(0); + break; + default: + break; + } } - static void MonetaryInteraction(AccountInteractionType accountInteractionType) + public static void DeleteAccount(AccountServices accountServices, GeneralServices generalServices, TransactionServices transactionServices) { - var parsedAccountCode = GatherNumericInput("Enter Account Number: "); - - var parsedMonetarylAmount = accountInteractionType == AccountInteractionType.Deposit ? GatherDoublelInput("Enter Amount to Deposit: ") : GatherDoublelInput("Enter Amount to Withdraw: "); - - if (parsedAccountCode != null && parsedMonetarylAmount != null) + try { - foreach (var account in accounts.Where(a => a.AccountCode == parsedAccountCode)) + //Gather + var accountCodeParsed = generalServices.GatherNumericInput("Enter Account Code: "); + + if (accountCodeParsed != null) { - var originalBalance = account.AccountBalance; - account.AccountBalance = accountInteractionType == AccountInteractionType.Deposit ? account.AccountBalance += parsedMonetarylAmount.Value : account.AccountBalance >= parsedMonetarylAmount ? account.AccountBalance -= parsedMonetarylAmount.Value : originalBalance; + //Confirm + var confirmed = generalServices.GatherTextualInput("Enter 'Y' to delete Account: "); - if (accountInteractionType == AccountInteractionType.Deposit) - DisplayMessage(MessageType.Success, "Deposit successful."); - else + if (confirmed.ToUpper() == "Y") { - if (account.AccountBalance == originalBalance) - DisplayMessage(MessageType.Error, "Insufficient balance."); + //Process + var success = accountServices.ProcessDeleteAccount(accountCodeParsed.Value); + if (success) + generalServices.DisplayMessage(MessageType.Success, $"Account : {accountCodeParsed} has been deleted."); else - DisplayMessage(MessageType.Success, "Withdrawal successful."); + generalServices.DisplayMessage(MessageType.Warning, $"Account : {accountCodeParsed} has NOT been deleted."); } + else + generalServices.DisplayMessage(MessageType.Warning, $"Account : {accountCodeParsed} has NOT been deleted by choice."); + } + else + { + generalServices.DisplayMessage(MessageType.Error, "Account Code was null - Please try again"); + } + } + catch (Exception e) + { + generalServices.DisplayMessage(MessageType.Error, "Exception Hit."); } - else - DisplayMessage(MessageType.Error, "Account not found."); } - - - static void DisplayAccountDetails() + public static void EditAccountName(AccountServices accountServices, GeneralServices generalServices, TransactionServices transactionServices) { - DisplayMessage(MessageType.Information, "Enter Account ID:"); - var resultParse = int.TryParse(Console.ReadLine(), out int result); + try + { + //Gather + var accountCodeParsed = generalServices.GatherNumericInput("Enter Account Code: "); - var id = result; + if (accountCodeParsed != null) + { + var newAccountNameParsed = generalServices.GatherTextualInput("Enter new Name: "); - foreach (var account in accounts) - { - if (account.Id == id) + //Process + var success = accountServices.ProcessEditAccountName(accountCodeParsed.Value, newAccountNameParsed); + if (success) + generalServices.DisplayMessage(MessageType.Success, $"Account Name updated."); + else + generalServices.DisplayMessage(MessageType.Warning, $"Account Name was not updated."); + + } + else { - DisplayMessage(MessageType.MenuItem, $"Account ID (Should be hidden): {account.Id}"); - DisplayMessage(MessageType.MenuItem, $"Account Code: {account.AccountCode}"); - DisplayMessage(MessageType.MenuItem, $"Account Holder: {account.AccountName}"); - DisplayMessage(MessageType.MenuItem, $"Balance: {account.AccountBalance}"); - return; + generalServices.DisplayMessage(MessageType.Error, "Account Code was null - Please try again"); } } - - DisplayMessage(MessageType.Error, "Account not found."); - } - - - #region Input - - private static void GatherInput(InputType numeric, string message) - { - switch (numeric) + catch (Exception e) { - case InputType.Numeric: - GatherNumericInput(message); - break; - case InputType.Textual: - GatherTextualInput(message); - break; - default: - break; + generalServices.DisplayMessage(MessageType.Error, "Exception Hit."); } } - static string? GatherTextualInput(string message) + public static void DisplayAccountTransactionHistory(AccountServices accountServices, GeneralServices generalServices, TransactionServices transactionServices) { - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write(message); - Console.ForegroundColor = ConsoleColor.White; - var rawInput = Console.ReadLine(); - - if (string.IsNullOrEmpty(rawInput)) + try { - DisplayMessage(MessageType.Error, "Please provide input"); - return null; + //Gather + var accountCodeParsed = generalServices.GatherNumericInput("Enter Account Code: "); + + if (accountCodeParsed != null) + { + //Process + var transactionLog = transactionServices.DisplayAccountTransactionHistoryDetails(accountCodeParsed.Value); + if (transactionLog.Length > 0) + generalServices.DisplayMessage(MessageType.Information, $"Account Transaction History : \n {transactionLog}"); + else + generalServices.DisplayMessage(MessageType.Warning, $"No recorded Transactions "); + + } + else + { + generalServices.DisplayMessage(MessageType.Error, "Account Code was null - Please try again"); + } } - else + catch (Exception e) { - return rawInput; + generalServices.DisplayMessage(MessageType.Error, "Exception Hit."); } } - static int? GatherNumericInput(string message) + public static void AddAccount(AccountServices accountServices, GeneralServices generalServices) { - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write(message); - Console.ForegroundColor = ConsoleColor.White; - var resultParse = int.TryParse(Console.ReadLine(), out int result); - Console.Write(Environment.NewLine); - if (resultParse) - { - var id = result; - return id; + try + { + //Gather + var accountCodeParsed = generalServices.GatherNumericInput("Enter Account Code: "); + + if (accountCodeParsed != null) + { + var accountNameParsed = generalServices.GatherTextualInput("Enter Account Holder Name: "); + + if (accountNameParsed != null) + { + //Process + var sucess = accountServices.ProcessAccount(accountCodeParsed.Value, accountNameParsed); + if (sucess) + generalServices.DisplayMessage(MessageType.Success, $"Account : {accountCodeParsed} has been added."); + else + generalServices.DisplayMessage(MessageType.Warning, $"Account : {accountCodeParsed} has NOT been added. Account code alreay exists."); + } + } + else + { + generalServices.DisplayMessage(MessageType.Error, "Account Code was null - Please try again"); + } } - else + catch (Exception e) { - DisplayMessage(MessageType.Error, "Value MUST be numeric."); + generalServices.DisplayMessage(MessageType.Error, "Exception Hit."); } - return null; - } - static double? GatherDoublelInput(string message) + public static void MonetaryInteraction(AccountInteractionType accountInteractionType, AccountServices accountServices, GeneralServices generalServices, TransactionServices transactionServices) { - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write(message); - Console.ForegroundColor = ConsoleColor.White; - var resultParse = double.TryParse(Console.ReadLine(), out double result); - if (resultParse) + try { - var returnValue = result; - if (returnValue == 0.00) + //Gather + var parsedAccountCode = generalServices.GatherNumericInput("Enter Account Code: "); + + //TRANSFER ONLY- Gather recipient + var parsedAccountCodeRecipient = accountInteractionType == AccountInteractionType.Tranfer ? generalServices.GatherNumericInput("Enter Recipient Account Code: ") : null; + + var parsedMonetarylAmount = accountInteractionType == AccountInteractionType.Deposit ? generalServices.GatherDoublelInput("Enter Amount to Deposit: ") : accountInteractionType == AccountInteractionType.Tranfer ? generalServices.GatherDoublelInput("Enter Amount to Transfer: ") : generalServices.GatherDoublelInput("Enter Amount to Withdraw: "); + + //Validate + if (parsedMonetarylAmount <= 0) { - DisplayMessage(MessageType.Error, "Value MUST include decimal place and numbers only."); - return null; + generalServices.DisplayMessage(MessageType.Warning, "Deposit, Withdrawal or Transfer cannot be equal to or less than 0.00 or 0"); + return; + } + + var success = false; + var transactionSuccess = false; + if (parsedAccountCode != null && parsedMonetarylAmount != null) + { + //Monetary Interaction Bus Processing + switch (accountInteractionType) + { + case AccountInteractionType.Deposit: + + success = accountServices.DepositAccount(parsedAccountCode.Value, parsedMonetarylAmount.Value); + if (success) + { + //Transactional Log + transactionSuccess = transactionServices.ProcessTransaction(parsedAccountCode.Value, parsedAccountCode.Value, parsedMonetarylAmount.Value, accountInteractionType); + generalServices.DisplayMessage(MessageType.Success, "Deposit successful."); + if (transactionSuccess) + generalServices.DisplayMessage(MessageType.Success, "Transaction record successful."); + else + generalServices.DisplayMessage(MessageType.Warning, "Transaction record unsuccessful."); + + } + else + generalServices.DisplayMessage(MessageType.Warning, "Deposit unsuccessful."); + break; + case AccountInteractionType.Withdraw: + + success = accountServices.WithdrawAccount(parsedAccountCode.Value, parsedMonetarylAmount.Value); + if (success) + { + //Transactional Log + transactionSuccess = transactionServices.ProcessTransaction(parsedAccountCode.Value, parsedAccountCode.Value, parsedMonetarylAmount.Value, accountInteractionType); + generalServices.DisplayMessage(MessageType.Success, "Withdrawal successful."); + if (transactionSuccess) + generalServices.DisplayMessage(MessageType.Success, "Transaction record successful."); + else + generalServices.DisplayMessage(MessageType.Warning, "Transaction record unsuccessful."); + } + else + generalServices.DisplayMessage(MessageType.Warning, "Withdrawal unsuccessful. Insufficient funds"); + break; + case AccountInteractionType.Tranfer: + + success = accountServices.TransferBetweenAccounts(parsedAccountCode.Value, parsedAccountCodeRecipient.Value, parsedMonetarylAmount.Value); + if (success) + { + //Transactional Log + transactionSuccess = transactionServices.ProcessTransaction(parsedAccountCode.Value, parsedAccountCodeRecipient.Value, parsedMonetarylAmount.Value, accountInteractionType); + generalServices.DisplayMessage(MessageType.Success, $"Transfer successful, you sent {parsedMonetarylAmount} to {parsedAccountCodeRecipient} "); + if (transactionSuccess) + generalServices.DisplayMessage(MessageType.Success, "Transaction record successful."); + else + generalServices.DisplayMessage(MessageType.Warning, "Transaction record unsuccessful."); + + } + else + generalServices.DisplayMessage(MessageType.Warning, "Transfer unuccessful, Insufficient funds or one or more accounts do not exist."); + break; + default: + break; + } + } else - return returnValue; + generalServices.DisplayMessage(MessageType.Error, "Account not found."); } - else + catch (Exception e) { - DisplayMessage(MessageType.Error, "Value MUST include decimal place and numbers only."); - } - return null; - - } - - #endregion + generalServices.DisplayMessage(MessageType.Error, "Exception Hit."); + } + } - #region Messages - static void DisplayMessage(MessageType type, string message) + public static void DisplayAccountDetails(AccountServices accountServices, GeneralServices generalServices) { - switch (type) + try { - case MessageType.Information: - DisplayInformationMessage(message); - break; - case MessageType.Success: - DisplaySuccessMessage(message); - break; - case MessageType.Warning: - DisplayWarningMessage(message); - break; - case MessageType.Error: - DisplayErrorMessage(message); - break; - case MessageType.MenuItem: - DisplayMenuMessage(message); - break; - case MessageType.Input: - DisplayInputMessage(message); - break; + //Gather + var parsedAccountCode = generalServices.GatherNumericInput("Enter Account Code: "); - default: - break; + //Validate + if (parsedAccountCode != null) + { + var account = accountServices.GetAccountDetails(parsedAccountCode.Value); + + if (account.Id != -1) + { + //Only show what we want them to see. + generalServices.DisplayMessage(MessageType.MenuItem, $"Account Code: {account.AccountCode}"); + generalServices.DisplayMessage(MessageType.MenuItem, $"Account Holder: {account.AccountName}"); + generalServices.DisplayMessage(MessageType.MenuItem, $"Balance: {account.AccountBalance.ToString("0.00")}"); + } + else + generalServices.DisplayMessage(MessageType.Error, "Account not found."); + } + else + generalServices.DisplayMessage(MessageType.Error, "Account Code not parseable."); + } + catch (Exception e) + { + generalServices.DisplayMessage(MessageType.Error, "Exception Hit."); } - } - static void DisplayInformationMessage(string message) - { - Console.WriteLine(message); - } - static void DisplayWarningMessage(string message) - { - Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine(message); - Console.WriteLine(""); - Console.ForegroundColor = ConsoleColor.White; - } - static void DisplayErrorMessage(string message) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(""); - Console.WriteLine(message); - Console.WriteLine(""); - Console.ForegroundColor = ConsoleColor.White; - } - static void DisplaySuccessMessage(string message) - { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(message); - Console.WriteLine(""); - Console.ForegroundColor = ConsoleColor.White; - } - static void DisplayMenuMessage(string message) - { - Console.ForegroundColor = ConsoleColor.Gray; - Console.WriteLine(message); - Console.ForegroundColor = ConsoleColor.White; - } - static void DisplayInputMessage(string message) - { - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write(message); - Console.ForegroundColor = ConsoleColor.White; } - #endregion - } - - class Account - { - public int Id { get; set; } - public int AccountCode {get; set; } - public string AccountName { get; set; } - public double AccountBalance { get; set; } } } diff --git a/README.md b/README.md index 5c4a3e8..d02f284 100644 --- a/README.md +++ b/README.md @@ -1 +1,528 @@ -# 1. Debugging and Refactoring \ No newline at end of file +# 1. Debugging and Refactoring + +I was able to complete the task + bonus tasks + other QoL improvements. + +I was unable to have the unit test project within the solution folder - it caused many reference errors +I was unable to have the unit test project within source control whilst the project was outside of the solution folder - however it works here. + +What I have done is zip the Unit Test Project. If you can unzip it one layer up from the solution folder that is where I have had 30/30 tests succeeded. + +If push comes to shove I have added the code at the bottom of this readme file. +If you add an nUnitTest project - add a reference from the test project to the banking tool - and add the code into the generated .cs file it will work. + +My assumptions are + +Everything Money based should be positive transactions from a user perspective. +AccountCodes should be numeric. +Account Id's should not be shown to the users. +Account Name should be limited to 15 characters. + +Whilst using the repository - service - front end architecture - the repository is still a List<> and will not persist. + +Transaction logs should be accessible by anyone using the system. + +AccountName should be editable. + +Account should be able to be deleted. + + +To run the application - download the code,open the solution in Visual Studio and hit F5. + +To run the Unit Tests - After following instructions above - right click the UnitTest project in Solution Explorer navigate to 'Run Tests' and left click. +There are 30 tests which all pass. + + + +TEST CODE------------------------------------------------------------------------------------------------------------------------------------------------- + + +using DebuggingAndRefactoringTask1.Models; +using DebuggingAndRefactoringTask1.ServiceLayer; +using static DebuggingAndRefactoringTask1.Enums.Enums; +using static DebuggingAndRefactoringTask1.Repository.AccountRepository; +using static DebuggingAndRefactoringTask1.Repository.TransactionRepository; + +namespace UnitTests +{ + public class Tests + { + [SetUp] + public void Setup() + { + accounts.Add(new Account { Id = 1, AccountName = "Mr Shaun", AccountCode = 1, AccountBalance = 0.00 }); + accounts.Add(new Account { Id = 2, AccountName = "Mr Scott", AccountCode = 2, AccountBalance = 0.00 }); + accounts.Add(new Account { Id = 99, AccountName = "Mr Ryan (delete)", AccountCode = 99, AccountBalance = 0.00 }); + + transactions.Add(new AccountTransaction { Id = 1, AccountCode = 1, AccountCodeTo = 1, Amount = 10, TranactionType = AccountInteractionType.Deposit,TransactionDateTime = DateTime.Now }); + transactions.Add(new AccountTransaction { Id = 1, AccountCode = 1, AccountCodeTo = 1, Amount = 50, TranactionType = AccountInteractionType.Withdraw, TransactionDateTime = DateTime.Now }); + transactions.Add(new AccountTransaction { Id = 1, AccountCode = 1, AccountCodeTo = 2, Amount = 16.50, TranactionType = AccountInteractionType.Tranfer, TransactionDateTime = DateTime.Now }); + } + + #region Delete Account + [Test] + public void DeleteAccount_Valid() + { + var originalAccountCount = accounts.Count; + + var accountServices = new AccountServices(); + accountServices.ProcessDeleteAccount(99); + + var newAccountCount = accounts.Count; + + if (newAccountCount < originalAccountCount) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void DeleteAccount_Invalid() + { + var originalAccountCount = accounts.Count; + + var accountServices = new AccountServices(); + accountServices.ProcessDeleteAccount(6); + + var newAccountCount = accounts.Count; + + if (originalAccountCount == newAccountCount) + Assert.Pass(); + else + Assert.Fail(); + } + + #endregion + + #region Add Account + [Test] + public void AddAccount_Valid() + { + var originalAccountCount = accounts.Count; + + var accountServices = new AccountServices(); + accountServices.ProcessAccount(4, "Mr Paul"); + + var newAccountCount = accounts.Count; + + if (originalAccountCount < newAccountCount) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void AddAccount_Invalid() + { + var originalAccountCount = accounts.Count; + + var accountServices = new AccountServices(); + accountServices.ProcessAccount(1, "Mr Paul"); + + var newAccountCount = accounts.Count; + + if (originalAccountCount == newAccountCount) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region Edit Account + [Test] + public void EditAccount_Valid() + { + var originalAccountName = accounts[0].AccountName; + + var accountServices = new AccountServices(); + accountServices.ProcessEditAccountName(1, "Mr Ryan"); + + var newAccountName = accounts[0].AccountName; + + if (originalAccountName != newAccountName) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void EditAccount_Invalid_Exists() + { + var originalAccountName = accounts[0].AccountName; + + var accountServices = new AccountServices(); + accountServices.ProcessAccount(1, "Intentionally large string to fail validation"); + + var newAccountName = accounts[0].AccountName; + + if (originalAccountName == newAccountName) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region Monetary Interaction Deposit + [Test] + public void DepositAccount_Valid() + { + var originalAccountBalance = accounts[0].AccountBalance; + + var accountServices = new AccountServices(); + accountServices.DepositAccount(1, 10); + + var newAccountBalance = accounts[0].AccountBalance; + + if (newAccountBalance == originalAccountBalance + 10) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void DepositAccount_Invalid() + { + var originalAccountBalance = accounts[0].AccountBalance; + + var accountServices = new AccountServices(); + accountServices.DepositAccount(1, 0.00); + + var newAccountBalance = accounts[0].AccountBalance; + + if (originalAccountBalance == newAccountBalance) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region Monetary Interaction Withdraw + [Test] + public void WithdrawAccount_Valid() + { + var accountServices = new AccountServices(); + + accounts[0].AccountBalance = 20; + var originalAccountBalance = accounts[0].AccountBalance; + + accountServices.WithdrawAccount(1, 10); + + var newAccountBalance = accounts[0].AccountBalance; + + if (newAccountBalance == originalAccountBalance - 10) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void WithdrawAccount_Invalid() + { + var accountServices = new AccountServices(); + + accounts[0].AccountBalance = 1.00; + var originalAccountBalance = accounts[0].AccountBalance; + + accountServices.WithdrawAccount(1, 2.00); + + var newAccountBalance = accounts[0].AccountBalance; + + if (originalAccountBalance == newAccountBalance) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region Monetary Interaction Transfer + [Test] + public void TransferAccount_Valid() + { + var accountServices = new AccountServices(); + + accounts[0].AccountBalance = 100; + accounts[1].AccountBalance = 0; + + var originalAccountBalance = accounts[0].AccountBalance; + var originalAccountToBalance = accounts[1].AccountBalance; + + accountServices.TransferBetweenAccounts(1,2, 50); + + var newAccountBalance = accounts[0].AccountBalance; + + var newAccountToBalance = accounts[1].AccountBalance; + + if (newAccountBalance == 50 && newAccountToBalance == 50) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void TransferAccount_Invalid() + { + var accountServices = new AccountServices(); + + accounts[0].AccountBalance = 50.00; + accounts[1].AccountBalance = 0.00; + var originalAccountBalance = accounts[0].AccountBalance; + var originalAccountToBalance = accounts[1].AccountBalance; + + accountServices.TransferBetweenAccounts(1, 2, 51.00); + + var newAccountBalance = accounts[0].AccountBalance; + var newAccountToBalance = accounts[1].AccountBalance; + + if (newAccountBalance == originalAccountBalance) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region GetAccount + [Test] + public void GetAccount_Valid() + { + var accountServices = new AccountServices(); + + var account = accountServices.GetAccountDetails(1); + + if (account.AccountCode != -1) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GetAccount_Invalid() + { + var accountServices = new AccountServices(); + + var account = accountServices.GetAccountDetails(78); + + if (account.Id == -1) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region Account Exists + [Test] + public void AccountExists_Valid() + { + var accountServices = new AccountServices(); + + var exists = accountServices.DoesAccountCodeExist(1); + + if (exists) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void AccountExists_Invalid() + { + var accountServices = new AccountServices(); + + var exists = accountServices.DoesAccountCodeExist(78); + + if (!exists) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + #region Transactions + [Test] + public void ProcessTransaction_Valid() + { + var transactionServices = new TransactionServices(); + + var success = transactionServices.ProcessTransaction(1, 1, 10, AccountInteractionType.Withdraw); + + if (success) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void DisplayTransaction_Valid() + { + var transactionServices = new TransactionServices(); + + var success = transactionServices.DisplayAccountTransactionHistoryDetails(1); + + if (success != string.Empty) + Assert.Pass(); + else + Assert.Fail(); + } + [Test] + public void DisplayTransaction_Invalid() + { + var transactionServices = new TransactionServices(); + + var success = transactionServices.DisplayAccountTransactionHistoryDetails(75); + + if (success == string.Empty) + Assert.Pass(); + else + Assert.Fail(); + } + + #endregion + + #region General Services + [Test] + public void GatherTextualInput_Valid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("Test")); + var success = generalServices.GatherTextualInput(""); + + if (success == "Test") + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GatherTextualInput_Valid_Numeric() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("15")); + var success = generalServices.GatherTextualInput(""); + + if (success == "15") + Assert.Pass(); + else + Assert.Fail(); + } + [Test] + public void GatherTextualInput_Invalid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("Intentionally long string to fail validation")); + var success = generalServices.GatherTextualInput(""); + + + if (success == null) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GatherNumericInput_Valid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("1")); + var success = generalServices.GatherNumericInput(""); + + if (success == 1) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GatherNumericInput_Invalid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("df")); + var success = generalServices.GatherNumericInput(""); + + + if (success == null) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GatherDecimalInput_Valid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("1")); + var success = generalServices.GatherDoublelInput(""); + + if (success == 1) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GatherDoubleInput_Valid_decimal_places() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("1.00")); + var success = generalServices.GatherDoublelInput(""); + + + if (success == 1.00) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void GatherDoubleInput_Invalid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("fg")); + var success = generalServices.GatherDoublelInput(""); + + + if (success == null) + Assert.Pass(); + else + Assert.Fail(); + } + + + [Test] + public void GatherMenuInput_Valid() + { + var generalServices = new GeneralServices(); + Console.SetIn(new StringReader("1")); + var success = generalServices.DisplayMenu(); + + + if (success == 1) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void IsNumeric_Valid() + { + var generalServices = new GeneralServices(); + var success = generalServices.IsNumeric("1"); + + if (success) + Assert.Pass(); + else + Assert.Fail(); + } + + [Test] + public void IsNumeric_Invalid() + { + var generalServices = new GeneralServices(); + var success = generalServices.IsNumeric("fg"); + + if (!success) + Assert.Pass(); + else + Assert.Fail(); + } + #endregion + + } +} \ No newline at end of file diff --git a/Repository/AccountRepository.cs b/Repository/AccountRepository.cs new file mode 100644 index 0000000..b84a0a6 --- /dev/null +++ b/Repository/AccountRepository.cs @@ -0,0 +1,59 @@ +using DebuggingAndRefactoringTask1.Models; +namespace DebuggingAndRefactoringTask1.Repository +{ + + public static class AccountRepository + { + public static List accounts = new List(); + + public static bool AddAccount(Account account) + { + try + { + accounts.Add(account); + return true; + } + catch (Exception e) + { + return false; + } + } + + public static bool DeleteAccount(int accountCode) + { + try + { + accounts.RemoveAll(r => r.AccountCode == accountCode); + + return true; + } + catch (Exception e) + { + return false; + } + } + + public static bool EditAccountName(int accountCode, string updatedName) + { + try + { + var account = GetAccount(accountCode); + account.AccountName = updatedName; + return true; + } + catch (Exception e) + { + return false; + } + } + public static Account GetAccount(int accountCode) + { + foreach (var account in accounts.Where(a => a.AccountCode == accountCode)) + { + return account; + } + + return new Account { Id = -1 }; + } + } +} diff --git a/Repository/TransactionRepository.cs b/Repository/TransactionRepository.cs new file mode 100644 index 0000000..2b9f8ba --- /dev/null +++ b/Repository/TransactionRepository.cs @@ -0,0 +1,35 @@ +using DebuggingAndRefactoringTask1.Models; + +namespace DebuggingAndRefactoringTask1.Repository +{ + public static class TransactionRepository + { + public static List transactions = new List(); + + public static bool AddAccountTransaction(AccountTransaction transaction) + { + try + { + transactions.Add(transaction); + return true; + } + catch (Exception e) + { + return false; + } + } + + public static string GetAccountTransactions(int accountCode) + { + string returnString = string.Empty; + + foreach (var transaction in transactions.Where(t => t.AccountCode == accountCode)) + { + //string interpolation for ease of reading. + returnString += $"On {transaction.TransactionDateTime.ToString("dd/MM/yyyy HH:mm:ss")} AccountCode(sender) {transaction.AccountCode} sent { transaction.Amount} to AccountCode(reciever) {transaction.AccountCodeTo}.\n"; + } + + return returnString; + } + } +} diff --git a/ServiceLayer/AccountServices.cs b/ServiceLayer/AccountServices.cs new file mode 100644 index 0000000..bf56912 --- /dev/null +++ b/ServiceLayer/AccountServices.cs @@ -0,0 +1,132 @@ +using DebuggingAndRefactoringTask1.Models; +using static DebuggingAndRefactoringTask1.Repository.AccountRepository; + +namespace DebuggingAndRefactoringTask1.ServiceLayer +{ + public class AccountServices + { + public bool ProcessAccount(int accountCode, string accountName) + { + //Validate + bool accountCodeExists = DoesAccountCodeExist(accountCode); + if (!accountCodeExists) + { + //Process + var success = AddAccount(new Account { Id = accounts.Count() + 1, AccountCode = accountCode, AccountName = accountName, AccountBalance = 0.00 }); + if (success) + return true; + else + return false; + } + else + return false; + } + + public bool DoesAccountCodeExist(int accountCode) + { + if (accounts.Count > 0) + { + foreach (var account in accounts.Where(a => a.AccountCode == accountCode)) + { + return true; + } + } + return false; + } + + public Account GetAccountDetails(int accountCode) + { + return GetAccount(accountCode); + } + + public bool DepositAccount(int accountCode, double amount) + { + var account = GetAccount(accountCode); + + if (account != null) + { + account.AccountBalance = account.AccountBalance += amount; + return true; + } + else + { + return false; + } + } + + public bool WithdrawAccount(int accountCode, double amount) + { + var account = GetAccount(accountCode); + var accountOriginalBalance = account.AccountBalance; + + if (account != null) + { + //Checks if there is sufficient balance - if not reset original balance. + account.AccountBalance = amount <= account.AccountBalance ? account.AccountBalance -= amount : account.AccountBalance = accountOriginalBalance; + + if (account.AccountBalance < accountOriginalBalance) + return true; + else + return false; + } + else + { + return false; + } + } + + public bool TransferBetweenAccounts(int accountCode, int accountCodeTo, double amount) + { + var account = GetAccount(accountCode); + var recipientAccount = GetAccount(accountCodeTo); + var accountOriginalBalance = account.AccountBalance; + + if (account != null && recipientAccount != null) + { + //Checks sender has sufficient funds before sending to recipient and updating balance. + recipientAccount.AccountBalance = amount <= account.AccountBalance ? recipientAccount.AccountBalance += amount : account.AccountBalance = accountOriginalBalance; + account.AccountBalance = amount <= account.AccountBalance ? account.AccountBalance -= amount : account.AccountBalance = accountOriginalBalance; + + if (account.AccountBalance < accountOriginalBalance) + return true; + else + return false; + } + else + { + return false; + } + } + + public bool ProcessDeleteAccount(int accountCode) + { + bool accountCodeExists = DoesAccountCodeExist(accountCode); + if (accountCodeExists) + { + var success = DeleteAccount(accountCode); + if (success) + return true; + else + return false; + } + else + return false; + } + + public bool ProcessEditAccountName(int accountCode, string? newAccountName) + { + bool accountCodeExists = DoesAccountCodeExist(accountCode); + + if (accountCodeExists) + { + var success = EditAccountName(accountCode, newAccountName); + if (success) + return true; + else + return false; + } + else + return false; + } + } +} diff --git a/ServiceLayer/GeneralServices.cs b/ServiceLayer/GeneralServices.cs new file mode 100644 index 0000000..67319b4 --- /dev/null +++ b/ServiceLayer/GeneralServices.cs @@ -0,0 +1,212 @@ +using static DebuggingAndRefactoringTask1.Enums.Enums; + +namespace DebuggingAndRefactoringTask1.ServiceLayer +{ + public class GeneralServices + { + #region Input + public string? GatherTextualInput(string message) + { + try + { + //Gather + Console.ForegroundColor = ConsoleColor.Gray; + Console.Write(message); + Console.ForegroundColor = ConsoleColor.White; + + var rawInput = Console.ReadLine(); + + //Validate + if (string.IsNullOrEmpty(rawInput)) + { + DisplayMessage(MessageType.Error, "Please provide input"); + return null; + } + else if (rawInput.Length > 15) + { + DisplayMessage(MessageType.Error, "Input too long at over 15 chars"); + return null; + } + else + { + return rawInput; + } + } + catch (Exception e) + { + DisplayMessage(MessageType.Error, "Exception Hit."); + return null; + + } + } + + public int? GatherNumericInput(string message) + { + try + { + //Gather + Console.ForegroundColor = ConsoleColor.Gray; + Console.Write(message); + Console.ForegroundColor = ConsoleColor.White; + + //Valiadate + var resultParse = int.TryParse(Console.ReadLine(), out int result); + Console.Write(Environment.NewLine); + if (resultParse && (result < 99999999 || result > 0)) + { + var numericInput = result; + return numericInput; + } + else + { + DisplayMessage(MessageType.Error, "Value MUST be numeric."); + } + return null; + } + catch (Exception e) + { + DisplayMessage(MessageType.Error, "Exception Hit."); + return null; + } + } + + public double? GatherDoublelInput(string message) + { + try + { + //Gather + Console.ForegroundColor = ConsoleColor.Gray; + Console.Write(message); + Console.ForegroundColor = ConsoleColor.White; + + //Validate + var resultParse = double.TryParse(Console.ReadLine(), out double result); + Console.Write(Environment.NewLine); + if (resultParse && result <= double.MaxValue && result >= double.MinValue) + { + var returnValue = result; + if (returnValue == 0.00) + { + DisplayMessage(MessageType.Error, "Value MUST not be 0.00."); + return null; + } + else + return returnValue; + } + else + { + DisplayMessage(MessageType.Error, "Value MUST include decimal place and numbers only."); + } + return null; + } + catch (Exception e) + { + DisplayMessage(MessageType.Error, "Exception Hit."); + return null; + } + } + #endregion + + #region Messages + public int? DisplayMenu() + { + //Display Menu Items + DisplayMessage(MessageType.Information, $"Options:"); + DisplayMessage(MessageType.MenuItem, $"1. Add Account"); + DisplayMessage(MessageType.MenuItem, $"2. Deposit Money"); + DisplayMessage(MessageType.MenuItem, $"3. Withdraw Money"); + DisplayMessage(MessageType.MenuItem, $"4. Display Account Details"); + DisplayMessage(MessageType.MenuItem, $"5. Display Account Transaction History"); + DisplayMessage(MessageType.MenuItem, $"6. Transfer between accounts"); + DisplayMessage(MessageType.MenuItem, $"7. Edit Account Name"); + DisplayMessage(MessageType.MenuItem, $"8. DELETE ACCOUNT"); + DisplayMessage(MessageType.MenuItem, $"9. Exit \n"); + + //Parse input + var choiceParsed = GatherNumericInput($"Choice: "); + + //Validation of choice + if (choiceParsed > 9 || choiceParsed < 1) + { + DisplayMessage(MessageType.Error, $"Options are between 1 and 9 plesae reselect."); + + //redisplay menu if invalid + return DisplayMenu(); + } + + return choiceParsed; + } + + public void DisplayMessage(MessageType type, string message) + { + //Message Bus for different MessageType + switch (type) + { + case MessageType.Information: + DisplayInformationMessage(message); + break; + case MessageType.Success: + DisplaySuccessMessage(message); + break; + case MessageType.Warning: + DisplayWarningMessage(message); + break; + case MessageType.Error: + DisplayErrorMessage(message); + break; + case MessageType.MenuItem: + DisplayMenuMessage(message); + break; + case MessageType.Input: + DisplayInputMessage(message); + break; + default: + break; + } + } + public static void DisplayInformationMessage(string message) + { + Console.WriteLine(message); + Console.WriteLine(""); + } + public static void DisplayWarningMessage(string message) + { + Console.ForegroundColor = ConsoleColor.DarkYellow; + Console.WriteLine(message); + Console.WriteLine(""); + Console.ForegroundColor = ConsoleColor.White; + } + public static void DisplayErrorMessage(string message) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(message); + Console.WriteLine(""); + Console.ForegroundColor = ConsoleColor.White; + } + public static void DisplaySuccessMessage(string message) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(message); + Console.WriteLine(""); + Console.ForegroundColor = ConsoleColor.White; + } + public static void DisplayMenuMessage(string message) + { + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine(message); + Console.ForegroundColor = ConsoleColor.White; + } + public static void DisplayInputMessage(string message) + { + Console.ForegroundColor = ConsoleColor.Gray; + Console.Write(message); + Console.ForegroundColor = ConsoleColor.White; + } + #endregion + + public bool IsNumeric(string value) + { + return value.All(char.IsNumber); + } + } +} diff --git a/ServiceLayer/TransactionServices.cs b/ServiceLayer/TransactionServices.cs new file mode 100644 index 0000000..0d2b401 --- /dev/null +++ b/ServiceLayer/TransactionServices.cs @@ -0,0 +1,22 @@ +using DebuggingAndRefactoringTask1.Models; +using static DebuggingAndRefactoringTask1.Enums.Enums; +using static DebuggingAndRefactoringTask1.Repository.TransactionRepository; + +namespace DebuggingAndRefactoringTask1.ServiceLayer +{ + public class TransactionServices + { + public bool ProcessTransaction(int accountCode, int accountCodeTo, double amount, AccountInteractionType accountInteractionType) + { + var success = AddAccountTransaction(new AccountTransaction { Id = transactions.Count() + 1, AccountCode = accountCode, AccountCodeTo = accountCodeTo, Amount = amount, TransactionDateTime = DateTime.Now, TranactionType = accountInteractionType }); + return success; + } + + public string DisplayAccountTransactionHistoryDetails(int accountCode) + { + //Gets log as \n separated string. + var value = GetAccountTransactions(accountCode); + return value; + } + } +} diff --git a/UnitTests.zip b/UnitTests.zip new file mode 100644 index 0000000..d1c6be5 Binary files /dev/null and b/UnitTests.zip differ