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"
+
+ }
+ }
+}