diff --git a/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj b/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj index b537fc2..a742ad3 100644 --- a/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj +++ b/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj @@ -1,16 +1,25 @@ - + + Debug AnyCPU - cd5d577e-bdc9-4dfc-ac6a-b1da474995f3 + {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3} Library Properties AnyCompany.Tests AnyCompany.Tests v4.6.1 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + true @@ -20,6 +29,9 @@ DEBUG;TRACE prompt 4 + SA1208 + bin\Debug\AnyCompany.Tests.xml + 7.1 pdbonly @@ -28,26 +40,58 @@ TRACE prompt 4 + 7.1 - - - - - - - - - - - - - - + + ..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + + + + + + - + + + + + + + stylecop.json + + + + + + + + + + {C7E15594-7D8F-4C18-9DD7-14F3FBB1572D} + AnyCompany + + + - + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/TechTest/AnyCompany.Tests/Class1.cs b/TechTest/AnyCompany.Tests/Class1.cs deleted file mode 100644 index 5957505..0000000 --- a/TechTest/AnyCompany.Tests/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AnyCompany.Tests -{ - public class Class1 - { - } -} diff --git a/TechTest/AnyCompany.Tests/CustomerServiceTests.cs b/TechTest/AnyCompany.Tests/CustomerServiceTests.cs new file mode 100644 index 0000000..16d0fda --- /dev/null +++ b/TechTest/AnyCompany.Tests/CustomerServiceTests.cs @@ -0,0 +1,98 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Tests +{ + using AnyCompany.Service; + using AnyCompany.Tests.MockRepository; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Linq; + + /// + /// Provides unit tests for the class. + /// + [TestClass] + public class CustomerServiceTests + { + /// + /// Load all customers with orders + /// Some customers exist without orders + /// Get back the customers, no orders. + /// + [TestMethod] + public void LoadAllCustomerWithOrders_ExistingCustomersNoOrders_GetCustomersNoOrders() + { + // Arrange + var customerRepository = new MockCustomerRepository(); + var orderRepository = new MockOrderRepository(); + var customerService = new CustomerService(customerRepository, orderRepository); + + // Act + var customersWithOrders = customerService.LoadAllCustomersWithOrders(); + + // Assert + Assert.IsTrue(customersWithOrders.Count() == 2); + foreach (var (customer, orders) in customersWithOrders) + { + Assert.IsTrue(orders.Count() == 0); + } + } + + /// + /// Load all customers with orders + /// Some customers exist with orders + /// Get back the customers, with correct number of orders. + /// + [TestMethod] + public void LoadAllCustomerWithOrders_ExistingCustomersWithOrders_GetCustomersCorrectOrderNumbers() + { + // Arrange + var customerRepository = new MockCustomerRepository(); + var orderRepository = new MockOrderRepository(); + var customerService = new CustomerService(customerRepository, orderRepository); + var allCustomers = customerRepository.LoadAll().ToList(); + + orderRepository.Save(new Order() + { + OrderId = default, + Amount = 10, + VAT = default, + CustomerId = allCustomers[0].CustomerId, + }); + + orderRepository.Save(new Order() + { + OrderId = default, + Amount = 20, + VAT = default, + CustomerId = allCustomers[0].CustomerId, + }); + + orderRepository.Save(new Order() + { + OrderId = default, + Amount = 30, + VAT = default, + CustomerId = allCustomers[1].CustomerId, + }); + + // Act + var customersWithOrders = customerService.LoadAllCustomersWithOrders(); + + // Assert + Assert.IsTrue(customersWithOrders.Count() == 2); + foreach (var (customer, orders) in customersWithOrders) + { + if (customer.CustomerId == allCustomers[0].CustomerId) + { + Assert.IsTrue(orders.Count() == 2); + } + else if (customer.CustomerId == allCustomers[1].CustomerId) + { + Assert.IsTrue(orders.Count() == 1); + } + } + } + } +} diff --git a/TechTest/AnyCompany.Tests/MockRepository/MockCustomerRepository.cs b/TechTest/AnyCompany.Tests/MockRepository/MockCustomerRepository.cs new file mode 100644 index 0000000..c111a47 --- /dev/null +++ b/TechTest/AnyCompany.Tests/MockRepository/MockCustomerRepository.cs @@ -0,0 +1,59 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Tests.MockRepository +{ + using AnyCompany.Repository; + using System; + using System.Collections.Generic; + + /// + /// Provides a mock implementation of ICustomerRepository for use in unit tests, that starts with two customers. + /// + internal class MockCustomerRepository : ICustomerRepository + { + private readonly List customers = new List(); + + /// + /// Initializes a new instance of the class. + /// + public MockCustomerRepository() + { + this.customers.Add(new Customer + { + CustomerId = 1, + Country = "UK", + DateOfBirth = new DateTime(1990, 01, 01), + Name = "John Doe", + }); + + this.customers.Add(new Customer + { + CustomerId = 2, + Country = "USA", + DateOfBirth = new DateTime(1980, 01, 01), + Name = "Jane Doe", + }); + } + + /// + public Customer Load(int customerId) + { + if (customerId > 0 && customerId <= 2) + { + return this.customers[customerId - 1]; + } + else + { + throw new ApplicationException($"Customer not found with id: {customerId}"); + } + } + + /// + public IEnumerable LoadAll() + { + return this.customers; + } + } +} diff --git a/TechTest/AnyCompany.Tests/MockRepository/MockOrderRepository.cs b/TechTest/AnyCompany.Tests/MockRepository/MockOrderRepository.cs new file mode 100644 index 0000000..7945be6 --- /dev/null +++ b/TechTest/AnyCompany.Tests/MockRepository/MockOrderRepository.cs @@ -0,0 +1,29 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Tests.MockRepository +{ + using AnyCompany.Repository; + using System.Collections.Generic; + + /// + /// Provides a mock implementation of IOrderRepository for use during unit testing, that starts out empty. + /// + internal class MockOrderRepository : IOrderRepository + { + private readonly List orders = new List(); + + /// + public void Save(Order order) + { + this.orders.Add(order); + } + + /// + public IEnumerable LoadAll() + { + return this.orders; + } + } +} diff --git a/TechTest/AnyCompany.Tests/OrderServiceTests.cs b/TechTest/AnyCompany.Tests/OrderServiceTests.cs new file mode 100644 index 0000000..021b121 --- /dev/null +++ b/TechTest/AnyCompany.Tests/OrderServiceTests.cs @@ -0,0 +1,78 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Tests +{ + using AnyCompany.Service; + using AnyCompany.Tests.MockRepository; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Linq; + + /// + /// Provides unit tests for the class. + /// + [TestClass] + public class OrderServiceTests + { + /// + /// Save + /// An order linked to a UK customer + /// VAT is 0.2d. + /// + [TestMethod] + public void PlaceOrder_LinkedToUkCustomer_VatIsTwentyPercent() + { + // Arrange + var orderRepository = new MockOrderRepository(); + var customerRepository = new MockCustomerRepository(); + var orderService = new OrderService(orderRepository, customerRepository); + var ukCustomer = customerRepository.LoadAll().First(); + + // Act + var order = new Order() + { + OrderId = default, + Amount = 20, + VAT = default, + CustomerId = ukCustomer.CustomerId, + }; + + orderService.PlaceOrder(order); + + // Assert + Assert.IsTrue(ukCustomer.Country == "UK"); + Assert.IsTrue(order.VAT == 0.2d); + } + + /// + /// Save + /// An order linked to a non-UK customer + /// VAT is 0. + /// + [TestMethod] + public void PlaceOrder_LinkedToNonUkCustomer_VatIsZero() + { + // Arrange + var orderRepository = new MockOrderRepository(); + var customerRepository = new MockCustomerRepository(); + var orderService = new OrderService(orderRepository, customerRepository); + var nonUkCustomer = customerRepository.LoadAll().Skip(1).First(); + + // Act + var order = new Order() + { + OrderId = default, + Amount = 20, + VAT = default, + CustomerId = nonUkCustomer.CustomerId, + }; + + orderService.PlaceOrder(order); + + // Assert + Assert.IsTrue(nonUkCustomer.Country != "UK"); + Assert.IsTrue(order.VAT == 0d); + } + } +} diff --git a/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs b/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs index 726eefa..ea5eabc 100644 --- a/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs +++ b/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs @@ -1,5 +1,8 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +// +// Copyright © Investec Bank 2018 +// + +using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/TechTest/AnyCompany.Tests/packages.config b/TechTest/AnyCompany.Tests/packages.config new file mode 100644 index 0000000..9a43b38 --- /dev/null +++ b/TechTest/AnyCompany.Tests/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/TechTest/AnyCompany/AnyCompany.csproj b/TechTest/AnyCompany/AnyCompany.csproj index 5b0498d..b84844e 100644 --- a/TechTest/AnyCompany/AnyCompany.csproj +++ b/TechTest/AnyCompany/AnyCompany.csproj @@ -20,6 +20,9 @@ DEBUG;TRACE prompt 4 + SA1208 + bin\Debug\AnyCompany.xml + 7.1 pdbonly @@ -28,10 +31,14 @@ TRACE prompt 4 + 7.1 + + ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + @@ -40,12 +47,26 @@ - - - - - + + + + + + + + + + + + stylecop.json + + + + + + + \ No newline at end of file diff --git a/TechTest/AnyCompany/Customer.cs b/TechTest/AnyCompany/Customer.cs deleted file mode 100644 index aa994b6..0000000 --- a/TechTest/AnyCompany/Customer.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace AnyCompany -{ - public class Customer - { - public string Country { get; set; } - - public DateTime DateOfBirth { get; set; } - - public string Name { get; set; } - } -} diff --git a/TechTest/AnyCompany/CustomerRepository.cs b/TechTest/AnyCompany/CustomerRepository.cs deleted file mode 100644 index e3de9b7..0000000 --- a/TechTest/AnyCompany/CustomerRepository.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Data.SqlClient; - -namespace AnyCompany -{ - public static class CustomerRepository - { - private static string ConnectionString = @"Data Source=(local);Database=Customers;User Id=admin;Password=password;"; - - public static Customer Load(int customerId) - { - Customer customer = new Customer(); - - SqlConnection connection = new SqlConnection(ConnectionString); - connection.Open(); - - SqlCommand command = new SqlCommand("SELECT * FROM Customer WHERE CustomerId = " + customerId, - connection); - var reader = command.ExecuteReader(); - - while (reader.Read()) - { - customer.Name = reader["Name"].ToString(); - customer.DateOfBirth = DateTime.Parse(reader["DateOfBirth"].ToString()); - customer.Country = reader["Country"].ToString(); - } - - connection.Close(); - - return customer; - } - } -} diff --git a/TechTest/AnyCompany/Model/Customer.cs b/TechTest/AnyCompany/Model/Customer.cs new file mode 100644 index 0000000..6029ccd --- /dev/null +++ b/TechTest/AnyCompany/Model/Customer.cs @@ -0,0 +1,34 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany +{ + using System; + + /// + /// The customer database model. + /// + public class Customer + { + /// + /// Gets or sets the unique id for this customer. + /// + public int CustomerId { get; set; } + + /// + /// Gets or sets a description of the country in which the customer lives. + /// + public string Country { get; set; } + + /// + /// Gets or sets this customer's date of birth. + /// + public DateTime DateOfBirth { get; set; } + + /// + /// Gets or sets this customer's full name. + /// + public string Name { get; set; } + } +} diff --git a/TechTest/AnyCompany/Model/Order.cs b/TechTest/AnyCompany/Model/Order.cs new file mode 100644 index 0000000..9865e17 --- /dev/null +++ b/TechTest/AnyCompany/Model/Order.cs @@ -0,0 +1,32 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany +{ + /// + /// The order database model. + /// + public class Order + { + /// + /// Gets or sets a unique identifier for this order. + /// + public int OrderId { get; set; } + + /// + /// Gets or sets the amount (in GBP?) this order is for. + /// + public double Amount { get; set; } + + /// + /// Gets or sets the rate of VAT (Value Added Tax) due on this order. + /// + public double VAT { get; set; } + + /// + /// Gets or sets the id of the customer who placed this order. + /// + public int CustomerId { get; set; } + } +} diff --git a/TechTest/AnyCompany/Order.cs b/TechTest/AnyCompany/Order.cs deleted file mode 100644 index fec8e7b..0000000 --- a/TechTest/AnyCompany/Order.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AnyCompany -{ - public class Order - { - public int OrderId { get; set; } - public double Amount { get; set; } - public double VAT { get; set; } - } -} diff --git a/TechTest/AnyCompany/OrderRepository.cs b/TechTest/AnyCompany/OrderRepository.cs deleted file mode 100644 index 3229885..0000000 --- a/TechTest/AnyCompany/OrderRepository.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Data.SqlClient; - -namespace AnyCompany -{ - internal class OrderRepository - { - private static string ConnectionString = @"Data Source=(local);Database=Orders;User Id=admin;Password=password;"; - - public void Save(Order order) - { - SqlConnection connection = new SqlConnection(ConnectionString); - connection.Open(); - - SqlCommand command = new SqlCommand("INSERT INTO Orders VALUES (@OrderId, @Amount, @VAT)", connection); - - command.Parameters.AddWithValue("@OrderId", order.OrderId); - command.Parameters.AddWithValue("@Amount", order.Amount); - command.Parameters.AddWithValue("@VAT", order.VAT); - - command.ExecuteNonQuery(); - - connection.Close(); - } - } -} diff --git a/TechTest/AnyCompany/OrderService.cs b/TechTest/AnyCompany/OrderService.cs deleted file mode 100644 index ebfb103..0000000 --- a/TechTest/AnyCompany/OrderService.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace AnyCompany -{ - public class OrderService - { - private readonly OrderRepository orderRepository = new OrderRepository(); - - public bool PlaceOrder(Order order, int customerId) - { - Customer customer = CustomerRepository.Load(customerId); - - if (order.Amount == 0) - return false; - - if (customer.Country == "UK") - order.VAT = 0.2d; - else - order.VAT = 0; - - orderRepository.Save(order); - - return true; - } - } -} diff --git a/TechTest/AnyCompany/Properties/AssemblyInfo.cs b/TechTest/AnyCompany/Properties/AssemblyInfo.cs index bcdfb17..2de047e 100644 --- a/TechTest/AnyCompany/Properties/AssemblyInfo.cs +++ b/TechTest/AnyCompany/Properties/AssemblyInfo.cs @@ -1,5 +1,8 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +// +// Copyright © Investec Bank 2018 +// + +using System.Reflection; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/TechTest/AnyCompany/Repository/CustomerRepository.cs b/TechTest/AnyCompany/Repository/CustomerRepository.cs new file mode 100644 index 0000000..e08f0ca --- /dev/null +++ b/TechTest/AnyCompany/Repository/CustomerRepository.cs @@ -0,0 +1,95 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Repository +{ + using System; + using System.Collections.Generic; + using System.Data.SqlClient; + + /// + /// This static class has been explicitly requested to remain in place. It provides the core functionality for + /// . + /// + internal static class CustomerRepository + { + /// + /// The connection string to use for connecting to the database. + /// + // TODO: The connection string should be built from injected config elements, not set statically in code. + private static readonly string ConnectionString = + @"Data Source=(local);Database=Customers;User Id=admin;Password=password;"; + + /// + /// Loads a cutomer by it's customerId. + /// + /// The id of the customer to load. + /// The customer with the given id, if one exists. + public static Customer Load(int customerId) + { + Customer customer = new Customer(); + + SqlConnection connection = new SqlConnection(ConnectionString); + connection.Open(); + + SqlCommand command = new SqlCommand( + "SELECT CustomerId, Name, DateOfBirth, Country FROM Customer WHERE CustomerId = " + customerId, + connection); + var reader = command.ExecuteReader(); + + if (!reader.HasRows) + { + throw new ApplicationException($"Customer not found with id: {customerId}"); + } + + reader.Read(); + + customer.CustomerId = int.Parse(reader["CustomerId"].ToString()); + customer.Name = reader["Name"].ToString(); + customer.DateOfBirth = DateTime.Parse(reader["DateOfBirth"].ToString()); + customer.Country = reader["Country"].ToString(); + + if (reader.Read()) + { + throw new ApplicationException($"More than one customer found with id: {customerId}"); + } + + connection.Close(); + + return customer; + } + + /// + /// Loads all customers. + /// + /// The set of all customers. + public static IEnumerable LoadAll() + { + var customers = new List(); + + SqlConnection connection = new SqlConnection(ConnectionString); + connection.Open(); + + SqlCommand command = + new SqlCommand("SELECT CustomerId, Name, DateOfBirth, Country FROM Customer", connection); + var reader = command.ExecuteReader(); + + while (reader.Read()) + { + var customer = new Customer + { + CustomerId = int.Parse(reader["CustomerId"].ToString()), + Name = reader["Name"].ToString(), + DateOfBirth = DateTime.Parse(reader["DateOfBirth"].ToString()), + Country = reader["Country"].ToString(), + }; + customers.Add(customer); + } + + connection.Close(); + + return customers; + } + } +} diff --git a/TechTest/AnyCompany/Repository/CustomerRepositoryInstance.cs b/TechTest/AnyCompany/Repository/CustomerRepositoryInstance.cs new file mode 100644 index 0000000..8e1143c --- /dev/null +++ b/TechTest/AnyCompany/Repository/CustomerRepositoryInstance.cs @@ -0,0 +1,27 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Repository +{ + using System.Collections.Generic; + + /// + /// An instantiable wrapper for the , providing a SqlClient implementation of + /// . + /// + public class CustomerRepositoryInstance : ICustomerRepository + { + /// + public Customer Load(int customerId) + { + return CustomerRepository.Load(customerId); + } + + /// + public IEnumerable LoadAll() + { + return CustomerRepository.LoadAll(); + } + } +} diff --git a/TechTest/AnyCompany/Repository/ICustomerRepository.cs b/TechTest/AnyCompany/Repository/ICustomerRepository.cs new file mode 100644 index 0000000..b616942 --- /dev/null +++ b/TechTest/AnyCompany/Repository/ICustomerRepository.cs @@ -0,0 +1,27 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Repository +{ + using System.Collections.Generic; + + /// + /// Provdies methods for loading (and in future saving) objects. + /// + public interface ICustomerRepository + { + /// + /// Loads a cutomer by it's customerId. + /// + /// The id of the customer to load. + /// The customer with the given id, if one exists. + Customer Load(int customerId); + + /// + /// Loads all customers. + /// + /// The set of all customers. + IEnumerable LoadAll(); + } +} diff --git a/TechTest/AnyCompany/Repository/IOrderRepository.cs b/TechTest/AnyCompany/Repository/IOrderRepository.cs new file mode 100644 index 0000000..2546487 --- /dev/null +++ b/TechTest/AnyCompany/Repository/IOrderRepository.cs @@ -0,0 +1,26 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Repository +{ + using System.Collections.Generic; + + /// + /// Provides methods for saving and retrieving orders. + /// + public interface IOrderRepository + { + /// + /// Saves a given order to the database. + /// + /// The order to save. + void Save(Order order); + + /// + /// Loads all orders. + /// + /// The set of all orders. + IEnumerable LoadAll(); + } +} diff --git a/TechTest/AnyCompany/Repository/OrderRepository.cs b/TechTest/AnyCompany/Repository/OrderRepository.cs new file mode 100644 index 0000000..4761216 --- /dev/null +++ b/TechTest/AnyCompany/Repository/OrderRepository.cs @@ -0,0 +1,66 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Repository +{ + using System.Collections.Generic; + using System.Data.SqlClient; + + /// + /// Provides a SqlClient implementation of the interface. + /// + internal class OrderRepository : IOrderRepository + { + // TODO: The connection string should be built from injected config elements, not set statically in code. + private static readonly string ConnectionString = + @"Data Source=(local);Database=Orders;User Id=admin;Password=password;"; + + /// + public void Save(Order order) + { + SqlConnection connection = new SqlConnection(ConnectionString); + connection.Open(); + + SqlCommand command = + new SqlCommand("INSERT INTO Orders VALUES (@OrderId, @Amount, @VAT, @CustomerId)", connection); + + command.Parameters.AddWithValue("@OrderId", order.OrderId); + command.Parameters.AddWithValue("@Amount", order.Amount); + command.Parameters.AddWithValue("@VAT", order.VAT); + command.Parameters.AddWithValue("@CustomerId", order.CustomerId); + + command.ExecuteNonQuery(); + + connection.Close(); + } + + /// + public IEnumerable LoadAll() + { + var orders = new List(); + + SqlConnection connection = new SqlConnection(ConnectionString); + connection.Open(); + + SqlCommand command = new SqlCommand("SELECT OrderId, Amount, VAT, CustomerId FROM Orders", connection); + var reader = command.ExecuteReader(); + + while (reader.Read()) + { + var order = new Order + { + OrderId = int.Parse(reader["OrderId"].ToString()), + Amount = double.Parse(reader["Amount"].ToString()), + VAT = double.Parse(reader["VAT"].ToString()), + CustomerId = int.Parse(reader["CustomerId"].ToString()), + }; + orders.Add(order); + } + + connection.Close(); + + return orders; + } + } +} diff --git a/TechTest/AnyCompany/Service/CustomerService.cs b/TechTest/AnyCompany/Service/CustomerService.cs new file mode 100644 index 0000000..06f38e7 --- /dev/null +++ b/TechTest/AnyCompany/Service/CustomerService.cs @@ -0,0 +1,48 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Service +{ + using AnyCompany.Repository; + using System.Collections.Generic; + using System.Linq; + + /// + /// Contains business level logic for interacting with customers, specifically customer loading functionality. + /// + public class CustomerService + { + private readonly ICustomerRepository customerRepository; + private readonly IOrderRepository orderRepository; + + /// + /// Initializes a new instance of the class. + /// + /// The customer repository. + /// The order repository. + public CustomerService(ICustomerRepository customerRepository, IOrderRepository orderRepository) + { + this.customerRepository = customerRepository; + this.orderRepository = orderRepository; + } + + /// + /// Loads all customers, along with their orders. + /// + /// A set of (customer, set of orders) tuples. + public IEnumerable<(Customer, IEnumerable)> LoadAllCustomersWithOrders() + { + var allCustomers = this.customerRepository.LoadAll(); + var allOrders = this.orderRepository.LoadAll(); + + var customerIdToOrdersMapping = allCustomers.ToDictionary(c => c.CustomerId, c => new List()); + foreach (var order in allOrders) + { + customerIdToOrdersMapping[order.CustomerId].Add(order); + } + + return allCustomers.Select(c => (c, customerIdToOrdersMapping[c.CustomerId].AsEnumerable())); + } + } +} diff --git a/TechTest/AnyCompany/Service/OrderService.cs b/TechTest/AnyCompany/Service/OrderService.cs new file mode 100644 index 0000000..ed86f9a --- /dev/null +++ b/TechTest/AnyCompany/Service/OrderService.cs @@ -0,0 +1,56 @@ +// +// Copyright © Investec Bank 2018 +// + +namespace AnyCompany.Service +{ + using AnyCompany.Repository; + + /// + /// Contains business level logic for interacting with orders, specifically providing order placing functionality. + /// + public class OrderService + { + private readonly IOrderRepository orderRepository; + private readonly ICustomerRepository customerRepository; + + /// + /// Initializes a new instance of the class. + /// + /// The order repository. + /// The customer repository. + public OrderService(IOrderRepository orderRepository, ICustomerRepository customerRepository) + { + this.orderRepository = orderRepository; + this.customerRepository = customerRepository; + } + + /// + /// Attaches a given order to the customer with the given customerId. + /// + /// The order to be placed. + /// True if the order is placed successfully, false otherwise. + public bool PlaceOrder(Order order) + { + Customer customer = this.customerRepository.Load(order.CustomerId); + + if (order.Amount == 0) + { + return false; + } + + if (customer.Country == "UK") + { + order.VAT = 0.2d; + } + else + { + order.VAT = 0; + } + + this.orderRepository.Save(order); + + return true; + } + } +} diff --git a/TechTest/AnyCompany/packages.config b/TechTest/AnyCompany/packages.config new file mode 100644 index 0000000..530e2bd --- /dev/null +++ b/TechTest/AnyCompany/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/TechTest/stylecop.json b/TechTest/stylecop.json new file mode 100644 index 0000000..ead6185 --- /dev/null +++ b/TechTest/stylecop.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Investec Bank", + "copyrightText": "Copyright © Investec Bank 2018" + + } + } +}