diff --git a/ALM.Test/ALM.Test.csproj b/ALM.Test/ALM.Test.csproj index 26e7570..cba813e 100644 --- a/ALM.Test/ALM.Test.csproj +++ b/ALM.Test/ALM.Test.csproj @@ -8,9 +8,14 @@ + + + + + diff --git a/ALM.Test/TransactionTests.cs b/ALM.Test/TransactionTests.cs new file mode 100644 index 0000000..6c2dc22 --- /dev/null +++ b/ALM.Test/TransactionTests.cs @@ -0,0 +1,70 @@ +using ALM.Web; +using ALM.Web.Models; +using Shouldly; +using System; +using Xunit; + +namespace ALM.Test +{ + //public class Fixture + //{ + // public BankRepository Repository { get; set; } + // public Transactioner TransactionManager { get; set; } + + // public Fixture() + // { + // Repository = new BankRepository(); + // Repository.Initialize(); + // TransactionManager = new Transactioner(Repository); + // } + //} + + public class TransactionTests + { + [Theory] + [InlineData(1337)] + [InlineData(9000)] + [InlineData(80085)] + public void DepositTest(decimal amount) + { + var account = new Account(); + var transactioner = new Transactioner(); + decimal originalBalance = account.Balance; + + transactioner.Deposit(account, amount); + + account.Balance.ShouldBe(originalBalance + amount); + } + + [Theory] + [InlineData(1337, 500)] + [InlineData(9000, 100)] + [InlineData(80085, 80085)] + public void WithdrawTest(decimal initial, decimal amount) + { + var account = new Account(); + account.Credit(initial); + var transactioner = new Transactioner(); + decimal originalBalance = account.Balance; + + transactioner.Deposit(account, amount); + + account.Balance.ShouldBe(originalBalance + amount); + } + + [Theory] + [InlineData(1337, 1338)] + [InlineData(1111.111, 9999.999)] + [InlineData(1, 1.000000001)] + [InlineData(0, 1)] + public void WithdrawMoreThanBalance(decimal initial, decimal amount) + { + var account = new Account(); + account.Credit(initial); + var transactioner = new Transactioner(); + decimal originalBalance = account.Balance; + + Should.Throw(() => transactioner.Withdraw(account, amount)); + } + } +} diff --git a/ALM.Test/UnitTest1.cs b/ALM.Test/UnitTest1.cs deleted file mode 100644 index 1224eb0..0000000 --- a/ALM.Test/UnitTest1.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Xunit; - -namespace ALM.Test -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} diff --git a/ALM.Web/ALM.Web.csproj b/ALM.Web/ALM.Web.csproj index 8eacdc1..5e18e79 100644 --- a/ALM.Web/ALM.Web.csproj +++ b/ALM.Web/ALM.Web.csproj @@ -4,8 +4,14 @@ netcoreapp3.0 - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/ALM.Web/BankRepository.cs b/ALM.Web/BankRepository.cs index 97729fa..5209bb8 100644 --- a/ALM.Web/BankRepository.cs +++ b/ALM.Web/BankRepository.cs @@ -41,5 +41,15 @@ public Account AddAccount(Customer owner) return account; } + public Account GetAccount(int accountId) + { + var account = Accounts.FirstOrDefault(a => a.AccountId == accountId); + if (account == null) + { + throw new AccountNotFoundException(accountId); + } + return account; + } + } } diff --git a/ALM.Web/Controllers/HomeController.cs b/ALM.Web/Controllers/HomeController.cs index 66beb19..00f65db 100644 --- a/ALM.Web/Controllers/HomeController.cs +++ b/ALM.Web/Controllers/HomeController.cs @@ -28,11 +28,6 @@ public IActionResult Index() return View(model); } - public IActionResult Privacy() - { - return View(); - } - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { diff --git a/ALM.Web/Controllers/TransactionController.cs b/ALM.Web/Controllers/TransactionController.cs new file mode 100644 index 0000000..0187c3c --- /dev/null +++ b/ALM.Web/Controllers/TransactionController.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ALM.Web.Models; +using Microsoft.AspNetCore.Mvc; + +namespace ALM.Web.Controllers +{ + public class TransactionController : Controller + { + private readonly Transactioner _transactioner; + private readonly BankRepository _repository; + + public TransactionController(Transactioner transactioner, BankRepository repository) + { + _transactioner = transactioner; + _repository = repository; + } + + [HttpGet] + public IActionResult Index() + { + return RedirectToAction("Transact"); + } + + [HttpGet] + public IActionResult Transact() + { + var model = new TransactionViewModel(); + return View(model); + } + + [HttpPost] + public IActionResult Transact(TransactionViewModel model, TransactionType type) + { + if (!ModelState.IsValid) + { + return View(model); + } + try + { + var account = _repository.GetAccount(model.AccountId); + model.Account = account; + switch (type) + { + case TransactionType.Deposit: + _transactioner.Deposit(account, model.Amount); + break; + case TransactionType.Withdrawal: + _transactioner.Withdraw(account, model.Amount); + break; + default: + break; + } + } + catch (AccountNotFoundException ex) + { + ModelState.AddModelError("account", ex.Message); + return View(model); + } + catch (InvalidTransactionException ex) + { + ModelState.AddModelError("balance", ex.Message); + return View(model); + } + + return View(model); + } + + } +} \ No newline at end of file diff --git a/ALM.Web/Exceptions/AccountNotFoundException.cs b/ALM.Web/Exceptions/AccountNotFoundException.cs new file mode 100644 index 0000000..4f398de --- /dev/null +++ b/ALM.Web/Exceptions/AccountNotFoundException.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ALM.Web +{ + public class AccountNotFoundException : Exception + { + public AccountNotFoundException(int accountId) : base($"Account #{accountId} not found") + { + } + } +} diff --git a/ALM.Web/Exceptions/InvalidTransactionException.cs b/ALM.Web/Exceptions/InvalidTransactionException.cs new file mode 100644 index 0000000..a2e058c --- /dev/null +++ b/ALM.Web/Exceptions/InvalidTransactionException.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ALM.Web +{ + public class InvalidTransactionException:Exception + { + public InvalidTransactionException(string message) : base(message) + { + } + } +} diff --git a/ALM.Web/Models/TransactionViewModel.cs b/ALM.Web/Models/TransactionViewModel.cs new file mode 100644 index 0000000..1d461ea --- /dev/null +++ b/ALM.Web/Models/TransactionViewModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace ALM.Web.Models +{ + public class TransactionViewModel + { + [Required] + [Display(Name = "Account ID")] + public int AccountId { get; set; } = 0; + + [Required] + [Display(Name = "Amount")] + [DataType(DataType.Currency)] + public decimal Amount { get; set; } = 0; + + public Account Account { get; set; } + } +} diff --git a/ALM.Web/Startup.cs b/ALM.Web/Startup.cs index 98add4c..41b8b7b 100644 --- a/ALM.Web/Startup.cs +++ b/ALM.Web/Startup.cs @@ -25,6 +25,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddSingleton(); + services.AddScoped(); } diff --git a/ALM.Web/TransactionType.cs b/ALM.Web/TransactionType.cs new file mode 100644 index 0000000..ee4de8d --- /dev/null +++ b/ALM.Web/TransactionType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ALM.Web +{ + public enum TransactionType + { + Deposit, + Withdrawal + } +} diff --git a/ALM.Web/Transactioner.cs b/ALM.Web/Transactioner.cs new file mode 100644 index 0000000..50e4a19 --- /dev/null +++ b/ALM.Web/Transactioner.cs @@ -0,0 +1,62 @@ +using ALM.Web.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ALM.Web +{ + public class Transactioner + { + private readonly BankRepository _repository; + + public Transactioner(BankRepository repository) + { + _repository = repository; + } + + public Transactioner() + { + + } + + public decimal Deposit(Account account, decimal amount) + { + account.Credit(amount); + return account.Balance; + } + + public decimal Deposit(int accountId, decimal amount) + { + var account = _repository.GetAccount(accountId); + if (account == null) + { + throw new AccountNotFoundException(accountId); + } + return Deposit(account, amount); + } + + + public decimal Withdraw(Account account, decimal amount) + { + if (account.Balance < amount) + { + throw new InvalidTransactionException($"Cant Withdraw {amount} from account #{account.AccountId}. Balance is only {account.Balance}."); + } + account.Debit(amount); + return account.Balance; + } + + public decimal Withdraw(int accountId, decimal amount) + { + var account = _repository.GetAccount(accountId); + if (account == null) + { + throw new AccountNotFoundException(accountId); + } + return Withdraw(account, amount); + } + + + } +} diff --git a/ALM.Web/Views/Home/Privacy.cshtml b/ALM.Web/Views/Home/Privacy.cshtml deleted file mode 100644 index af4fb19..0000000 --- a/ALM.Web/Views/Home/Privacy.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -@{ - ViewData["Title"] = "Privacy Policy"; -} -

@ViewData["Title"]

- -

Use this page to detail your site's privacy policy.

diff --git a/ALM.Web/Views/Shared/_Layout.cshtml b/ALM.Web/Views/Shared/_Layout.cshtml index e1ba017..c906c59 100644 --- a/ALM.Web/Views/Shared/_Layout.cshtml +++ b/ALM.Web/Views/Shared/_Layout.cshtml @@ -18,11 +18,12 @@ diff --git a/ALM.Web/Views/Transaction/Transact.cshtml b/ALM.Web/Views/Transaction/Transact.cshtml new file mode 100644 index 0000000..4313778 --- /dev/null +++ b/ALM.Web/Views/Transaction/Transact.cshtml @@ -0,0 +1,24 @@ +@model TransactionViewModel +@{ + ViewData["Title"] = "Index"; +} + +

Transaction Time!

+ +
+ + + + +
+ +

@ViewBag.Message

+ +@if (Model.Account != null) +{ +

The new balance of account #@Model.Account.AccountId is now @Model.Account.Balance

+} +
+ + +