diff --git a/Solution.Txt b/Solution.Txt
new file mode 100644
index 0000000..2868323
--- /dev/null
+++ b/Solution.Txt
@@ -0,0 +1,38 @@
+- Solution is not fully completed however only completed to the extent to prove the approach and concept.
+- Dependency injection is used heavily to make is losley coupled and easy to test/mock.
+- "static" class declarations is not changed.
+- Only basic test cases has been added to prove the point.
+
+CustomerDbContext - NEW:
+ 1. ADO.Net is replaced by EF
+
+IUnitOfWork/UnitOfWork - NEW:
+ 1. New interface and class has been added.
+
+CustomerOrder - NEW:
+ 1. Mapping object, this contains the customer and it's orders if it has any.
+
+VAT - NEW:
+ 1. This is introduced to follow OCP.
+
+App.config - NEW:
+ 1. Connection string is moved here.
+
+CustomerRepository:
+ 1. Multiline ADO.net code is replaced by EF
+ 2. Load All customers implemented
+
+OrderRepository:
+ 1. Code is refactored to make it clean, readable and testable.
+
+OrderService:
+ 1. Refactored to make is extensible/OCP, clean, readable and testable.
+
+Test - NEW:
+ 1. CustomerRepositoryTest
+ 2. OrderRepositoryTest
+ 3. UnitOfWorkTest
+ 4. OrderServiceTest
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj b/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj
index b537fc2..1f98799 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
+ {1983942E-3221-4387-AE22-28B57C38ECCA}
Library
Properties
AnyCompany.Tests
AnyCompany.Tests
- v4.6.1
+ v4.7.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
@@ -30,24 +39,63 @@
4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ ..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll
+
+
+ ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll
+
+
+ ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll
+
+
+ ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+ ..\packages\Moq.4.10.1\lib\net45\Moq.dll
+
+
+
+
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
+
-
+
+
+
+
+
+
+ App.config
+ PreserveNewest
+
+
+
+
+
+ {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/App.config b/TechTest/AnyCompany.Tests/App.config
new file mode 100644
index 0000000..a6a2b7f
--- /dev/null
+++ b/TechTest/AnyCompany.Tests/App.config
@@ -0,0 +1,2 @@
+
+
\ 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/CustomerRepositoryTest.cs b/TechTest/AnyCompany.Tests/CustomerRepositoryTest.cs
new file mode 100644
index 0000000..3db2ad5
--- /dev/null
+++ b/TechTest/AnyCompany.Tests/CustomerRepositoryTest.cs
@@ -0,0 +1,70 @@
+using AnyCompany.Entity;
+using AnyCompany.Repository;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AnyCompany.Tests
+{
+ [TestClass]
+ public class CustomerRepositoryTest
+ {
+ private List CustomerList = new List();
+ private Mock unitOfWork = new Mock();
+
+ public CustomerRepositoryTest()
+ {
+ CustomerList.Add(
+ new Customer()
+ {
+ CustomerId = 1,
+ Country = "UK",
+ Name = "UKCustomer",
+ DateOfBirth = new DateTime(1970, 12, 23)
+ });
+ CustomerList.Add(
+ new Customer()
+ {
+ CustomerId = 2,
+ Country = "US",
+ Name = "USCustomer",
+ DateOfBirth = new DateTime(1970, 12, 24)
+ });
+
+ unitOfWork.Setup(s => s.Customers).Returns(CustomerList);
+ }
+
+ [TestMethod]
+ [TestCategory("Customer Repository: Positive Scenario only")]
+ public void Load_ReturnACustomer()
+ {
+ // Arrange
+ var expectedCustomer = CustomerList.SingleOrDefault(c => c.CustomerId == 1);
+
+ // Act
+ var customer = CustomerRepository.Load(expectedCustomer.CustomerId, unitOfWork.Object);
+
+ // Assert
+ unitOfWork.Verify(v => v.Customers, Times.Once);
+ Assert.AreEqual(expectedCustomer.CustomerId, customer.CustomerId);
+ }
+
+
+ [TestMethod]
+ [TestCategory("Customer Repository: Positive Scenario only")]
+ public void Load_ReturnAllCustomers()
+ {
+ // Arrange
+ var expectedCustomerCount = CustomerList.Count;
+
+ // Act
+ var customers = CustomerRepository.LoadAll(unitOfWork.Object).ToList();
+
+ // Assert
+ unitOfWork.Verify(v => v.Customers, Times.Once);
+ Assert.AreEqual(expectedCustomerCount, customers.Count);
+ }
+ }
+}
diff --git a/TechTest/AnyCompany.Tests/OrderRepositoryTest.cs b/TechTest/AnyCompany.Tests/OrderRepositoryTest.cs
new file mode 100644
index 0000000..c098add
--- /dev/null
+++ b/TechTest/AnyCompany.Tests/OrderRepositoryTest.cs
@@ -0,0 +1,36 @@
+using AnyCompany.Entity;
+using AnyCompany.Repository;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using System.Collections.Generic;
+
+namespace AnyCompany.Tests
+{
+ [TestClass]
+ public class OrderRepositoryTest
+ {
+ private Mock unitOfWork = new Mock();
+
+ public OrderRepositoryTest()
+ {
+ unitOfWork.Setup(s => s.Orders).Returns(new List());
+ unitOfWork.Setup(s => s.SaveChangesAsync());
+ }
+
+ [TestMethod]
+ [TestCategory("Order Repository: Positive Scenario only")]
+ public void Save_SaveAnOrder()
+ {
+ // Arrange
+ var order = new Order() { CustomerId = 1, OrderId = 1, Amount = 1d, VAT = 0 };
+ OrderRepository orderRepository = new OrderRepository(unitOfWork.Object);
+
+ // Act
+ orderRepository.Save(order);
+
+ // Assert
+ unitOfWork.Verify(v => v.Orders, Times.Once);
+ unitOfWork.Verify(v => v.SaveChangesAsync(), Times.Once);
+ }
+ }
+}
diff --git a/TechTest/AnyCompany.Tests/OrderServiceTest.cs b/TechTest/AnyCompany.Tests/OrderServiceTest.cs
new file mode 100644
index 0000000..f841a88
--- /dev/null
+++ b/TechTest/AnyCompany.Tests/OrderServiceTest.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using AnyCompany.Entity;
+using AnyCompany.Repository;
+using AnyCompany.Service;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+
+namespace AnyCompany.Tests
+{
+ [TestClass]
+ public class OrderServiceTest
+ {
+ private List CustomerList = new List();
+ private List VatList = new List();
+
+ private Mock unitOfWork = new Mock();
+
+ public OrderServiceTest()
+ {
+ CustomerList.Add(
+ new Customer()
+ {
+ CustomerId = 1,
+ Country = "UK",
+ Name = "UKCustomer",
+ DateOfBirth = new DateTime(1970, 12, 23)
+ });
+ CustomerList.Add(
+ new Customer()
+ {
+ CustomerId = 2,
+ Country = "US",
+ Name = "USCustomer",
+ DateOfBirth = new DateTime(1970, 12, 24)
+ });
+
+ unitOfWork.Setup(s => s.Customers).Returns(CustomerList);
+
+ // Add items to the VAT list
+ VatList.Add(
+ new VAT()
+ {
+ ApplyVat = .2d,
+ Country = "UK"
+ });
+ VatList.Add(new VAT()
+ {
+ ApplyVat = 0d,
+ Country = "US"
+ });
+
+ unitOfWork.Setup(s => s.VATs).Returns(VatList);
+ unitOfWork.Setup(s => s.Orders).Returns(new List());
+ unitOfWork.Setup(s => s.SaveChangesAsync());
+ }
+
+ [TestMethod]
+ [TestCategory("Order Service: Positive Scenario - UK Customer")]
+ public void Save_SaveAnOrderForUKCustomer()
+ {
+ // Arrange
+ bool expectedOutput = true;
+ var order = new Order() { CustomerId = 1, OrderId = 1, Amount = 1d, VAT = 0 };
+ OrderService orderRepository = new OrderService(unitOfWork.Object);
+
+ // Act
+ var actualOutput = orderRepository.PlaceOrder(order);
+
+ // Assert
+ unitOfWork.Verify(v => v.Customers, Times.Once);
+ unitOfWork.Verify(v => v.VATs, Times.Once);
+ unitOfWork.Verify(v => v.Orders, Times.Once);
+ unitOfWork.Verify(v => v.SaveChangesAsync(), Times.Once);
+ Assert.AreEqual(expectedOutput, actualOutput);
+ }
+
+
+ [TestMethod]
+ [TestCategory("Order Service: Positive Scenario - US Customer")]
+ public void Save_SaveAnOrderForUSCustomer()
+ {
+ // Arrange
+ bool expectedOutput = true;
+ var order = new Order() { CustomerId = 2, OrderId = 2, Amount = 1d, VAT = 0 };
+ OrderService orderRepository = new OrderService(unitOfWork.Object);
+
+ // Act
+ var actualOutput = orderRepository.PlaceOrder(order);
+
+ // Assert
+ unitOfWork.Verify(v => v.Customers, Times.Once);
+ unitOfWork.Verify(v => v.VATs, Times.Once);
+ unitOfWork.Verify(v => v.Orders, Times.Once);
+ unitOfWork.Verify(v => v.SaveChangesAsync(), Times.Once);
+ Assert.AreEqual(expectedOutput, actualOutput);
+ }
+
+
+
+ [TestMethod]
+ [TestCategory("Order Service: Positive Scenario only")]
+ public void IsValidOrder_ReturnTrue()
+ {
+ // Arrange
+ bool expectedOutput = true;
+ var order = new Order() { CustomerId = 2, OrderId = 2, Amount = 1d, VAT = 0 };
+ OrderService orderRepository = new OrderService(unitOfWork.Object);
+
+ // Act
+ var actualOutput = orderRepository.IsValidOrder(order);
+
+ // Assert
+ Assert.AreEqual(expectedOutput, actualOutput);
+ }
+ }
+}
diff --git a/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs b/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs
index 726eefa..2410ae1 100644
--- a/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs
+++ b/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs
@@ -1,36 +1,20 @@
-using System.Reflection;
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
[assembly: AssemblyTitle("AnyCompany.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Investec Bank")]
+[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AnyCompany.Tests")]
-[assembly: AssemblyCopyright("Copyright © Investec Bank 2018")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("cd5d577e-bdc9-4dfc-ac6a-b1da474995f3")]
+[assembly: Guid("1983942e-3221-4387-ae22-28b57c38ecca")]
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TechTest/AnyCompany.Tests/UnitOfWorkTest.cs b/TechTest/AnyCompany.Tests/UnitOfWorkTest.cs
new file mode 100644
index 0000000..9c2ca95
--- /dev/null
+++ b/TechTest/AnyCompany.Tests/UnitOfWorkTest.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity;
+using System.Linq;
+using AnyCompany.Context;
+using AnyCompany.Entity;
+using AnyCompany.Repository;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+
+namespace AnyCompany.Tests
+{
+ [TestClass]
+ public class UnitOfWorkTest
+ {
+ private Mock mockContext = new Mock();
+
+ private static Mock> CreateDbSetMock(IEnumerable elements) where T : class
+ {
+ var elementsAsQueryable = elements.AsQueryable();
+ var dbSetMock = new Mock>();
+
+ dbSetMock.As>().Setup(m => m.Provider).Returns(elementsAsQueryable.Provider);
+ dbSetMock.As>().Setup(m => m.Expression).Returns(elementsAsQueryable.Expression);
+ dbSetMock.As>().Setup(m => m.ElementType).Returns(elementsAsQueryable.ElementType);
+ dbSetMock.As>().Setup(m => m.GetEnumerator()).Returns(elementsAsQueryable.GetEnumerator());
+
+ return dbSetMock;
+ }
+
+ [TestMethod]
+ [TestCategory(" Unit Of Work: Positive Scenario only")]
+ public void Customers_ReturnAllCustomer()
+ {
+ // Arrange
+ var data = new List
+ {
+ new Customer(){
+ CustomerId = 1,
+ Country = "UK",
+ Name = "UKCustomer",
+ DateOfBirth = new DateTime(1970, 12, 23) }
+ };
+
+ mockContext.Setup(s => s.Customers).Returns(CreateDbSetMock(data).Object);
+
+ UnitOfWork unitOfWork = new UnitOfWork(mockContext.Object);
+
+ // Act
+ var customers = unitOfWork.Customers;
+
+ // Assert
+ mockContext.Verify(v => v.Customers, Times.Once());
+ Assert.AreEqual(data.Count(), customers.Count);
+ }
+
+ [TestMethod]
+ [TestCategory(" Unit Of Work: Positive Scenario only")]
+ public void Orders_ReturnAllOrder()
+ {
+ // Arrange
+ var data = new List
+ {
+ new Order(){
+ CustomerId = 1,
+ OrderId = 1,
+ Amount=1d,
+ VAT=0 }
+ };
+
+ mockContext.Setup(s => s.Orders).Returns(CreateDbSetMock(data).Object);
+
+ UnitOfWork unitOfWork = new UnitOfWork(mockContext.Object);
+
+ // Act
+ var orders = unitOfWork.Orders;
+
+ // Assert
+ mockContext.Verify(v => v.Orders, Times.Once());
+ Assert.AreEqual(data.Count(), orders.Count);
+ }
+
+
+ [TestMethod]
+ [TestCategory(" Unit Of Work: Positive Scenario only")]
+ public void VATs_ReturnAllVat()
+ {
+ // Arrange
+ var data = new List
+ {
+ new VAT(){
+ Country="UK",
+ ApplyVat=0.2d
+ }
+ };
+
+ mockContext.Setup(s => s.Vats).Returns(CreateDbSetMock(data).Object);
+
+ UnitOfWork unitOfWork = new UnitOfWork(mockContext.Object);
+
+ // Act
+ var vats = unitOfWork.VATs;
+
+ // Assert
+ mockContext.Verify(v => v.Vats, Times.Once());
+ Assert.AreEqual(data.Count(), vats.Count);
+ }
+
+ [TestMethod]
+ [TestCategory(" Unit Of Work: Positive Scenario only")]
+ public void CustomerOrders_LoadAllCustomersAndTheirLinkedOrders()
+ {
+ // Arrange
+ var customer = new List
+ {
+ new Customer(){
+ CustomerId = 1,
+ Country = "UK",
+ Name = "UKCustomer",
+ DateOfBirth = new DateTime(1970, 12, 23) },
+ new Customer(){
+ CustomerId = 2,
+ Country = "US",
+ Name = "USCustomer",
+ DateOfBirth = new DateTime(1970, 12, 20) }
+ };
+ mockContext.Setup(s => s.Customers).Returns(CreateDbSetMock(customer).Object);
+
+ var order = new List
+ {
+ new Order(){
+ CustomerId = 1,
+ OrderId = 1,
+ Amount=1d,
+ VAT=0 }
+ };
+ mockContext.Setup(s => s.Orders).Returns(CreateDbSetMock(order).Object);
+
+ UnitOfWork unitOfWork = new UnitOfWork(mockContext.Object);
+
+ // Act
+ var customerOrders = unitOfWork.CustomerOrders;
+
+ // Assert
+ mockContext.Verify(v => v.Customers, Times.Once());
+ mockContext.Verify(v => v.Orders, Times.Once());
+
+ // Total number of customers returned
+ Assert.AreEqual(customer.Count(), customerOrders.Count);
+
+ // Total number of order for customer 1
+ Assert.AreEqual(1, customerOrders.SingleOrDefault(c=>c.Customer.CustomerId==1).Orders.Count);
+
+ // Total number of order for customer 2
+ Assert.AreEqual(0, customerOrders.SingleOrDefault(c => c.Customer.CustomerId == 2).Orders.Count);
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany.Tests/packages.config b/TechTest/AnyCompany.Tests/packages.config
new file mode 100644
index 0000000..ec49d8a
--- /dev/null
+++ b/TechTest/AnyCompany.Tests/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany/AnyCompany.csproj b/TechTest/AnyCompany/AnyCompany.csproj
index 5b0498d..c9463ea 100644
--- a/TechTest/AnyCompany/AnyCompany.csproj
+++ b/TechTest/AnyCompany/AnyCompany.csproj
@@ -30,7 +30,15 @@
4
+
+ ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll
+
+
+ ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll
+
+
+
@@ -40,12 +48,24 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+ Designer
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany/App.config b/TechTest/AnyCompany/App.config
new file mode 100644
index 0000000..08ae4ea
--- /dev/null
+++ b/TechTest/AnyCompany/App.config
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany/Context/CustomerDbContext.cs b/TechTest/AnyCompany/Context/CustomerDbContext.cs
new file mode 100644
index 0000000..0969ac7
--- /dev/null
+++ b/TechTest/AnyCompany/Context/CustomerDbContext.cs
@@ -0,0 +1,14 @@
+using AnyCompany.Entity;
+using System.Data.Entity;
+
+namespace AnyCompany.Context
+{
+ public class CustomerDbContext : DbContext
+ {
+ public virtual DbSet Customers { get; set; }
+
+ public virtual DbSet Orders { get; set; }
+
+ public virtual DbSet Vats { 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/Customer.cs b/TechTest/AnyCompany/Entity/Customer.cs
similarity index 72%
rename from TechTest/AnyCompany/Customer.cs
rename to TechTest/AnyCompany/Entity/Customer.cs
index aa994b6..b0b1069 100644
--- a/TechTest/AnyCompany/Customer.cs
+++ b/TechTest/AnyCompany/Entity/Customer.cs
@@ -1,9 +1,11 @@
using System;
-namespace AnyCompany
+namespace AnyCompany.Entity
{
public class Customer
{
+ public int CustomerId { get; set; }
+
public string Country { get; set; }
public DateTime DateOfBirth { get; set; }
diff --git a/TechTest/AnyCompany/Entity/CustomerOrder.cs b/TechTest/AnyCompany/Entity/CustomerOrder.cs
new file mode 100644
index 0000000..6e76578
--- /dev/null
+++ b/TechTest/AnyCompany/Entity/CustomerOrder.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace AnyCompany.Entity
+{
+ public class CustomerOrder
+ {
+ public Customer Customer { get; set; }
+
+ public List Orders { get; set; }
+ }
+}
diff --git a/TechTest/AnyCompany/Order.cs b/TechTest/AnyCompany/Entity/Order.cs
similarity index 67%
rename from TechTest/AnyCompany/Order.cs
rename to TechTest/AnyCompany/Entity/Order.cs
index fec8e7b..7925549 100644
--- a/TechTest/AnyCompany/Order.cs
+++ b/TechTest/AnyCompany/Entity/Order.cs
@@ -1,9 +1,13 @@
-namespace AnyCompany
+namespace AnyCompany.Entity
{
public class Order
{
public int OrderId { get; set; }
+
+ public int CustomerId { get; set; }
+
public double Amount { get; set; }
+
public double VAT { get; set; }
}
}
diff --git a/TechTest/AnyCompany/Entity/VAT.cs b/TechTest/AnyCompany/Entity/VAT.cs
new file mode 100644
index 0000000..0fc7c37
--- /dev/null
+++ b/TechTest/AnyCompany/Entity/VAT.cs
@@ -0,0 +1,9 @@
+namespace AnyCompany.Entity
+{
+ public class VAT
+ {
+ public string Country { get; set; }
+
+ public double ApplyVat { 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/Repository/CustomerRepository.cs b/TechTest/AnyCompany/Repository/CustomerRepository.cs
new file mode 100644
index 0000000..f920c60
--- /dev/null
+++ b/TechTest/AnyCompany/Repository/CustomerRepository.cs
@@ -0,0 +1,19 @@
+using AnyCompany.Entity;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AnyCompany.Repository
+{
+ public static class CustomerRepository
+ {
+ public static Customer Load(int customerId, IUnitOfWork unitOfWork)
+ {
+ return unitOfWork.Customers.SingleOrDefault(c => c.CustomerId == customerId);
+ }
+
+ public static IEnumerable LoadAll(IUnitOfWork unitOfWork)
+ {
+ return unitOfWork.Customers.ToList();
+ }
+ }
+}
diff --git a/TechTest/AnyCompany/Repository/IUnitOfWork.cs b/TechTest/AnyCompany/Repository/IUnitOfWork.cs
new file mode 100644
index 0000000..307848b
--- /dev/null
+++ b/TechTest/AnyCompany/Repository/IUnitOfWork.cs
@@ -0,0 +1,18 @@
+using AnyCompany.Entity;
+using System.Collections.Generic;
+
+namespace AnyCompany.Repository
+{
+ public interface IUnitOfWork
+ {
+ List Customers { get; }
+
+ List Orders { get; }
+
+ List VATs { get; }
+
+ List CustomerOrders { get; }
+
+ void SaveChangesAsync();
+ }
+}
diff --git a/TechTest/AnyCompany/Repository/OrderRepository.cs b/TechTest/AnyCompany/Repository/OrderRepository.cs
new file mode 100644
index 0000000..2044535
--- /dev/null
+++ b/TechTest/AnyCompany/Repository/OrderRepository.cs
@@ -0,0 +1,21 @@
+using AnyCompany.Entity;
+
+namespace AnyCompany.Repository
+{
+ public class OrderRepository
+ {
+ private readonly IUnitOfWork unitOfWork;
+
+ public OrderRepository(IUnitOfWork UnitOfwork)
+ {
+ unitOfWork = UnitOfwork;
+ }
+
+ public void Save(Order order)
+ {
+ unitOfWork.Orders.Add(order);
+
+ unitOfWork.SaveChangesAsync();
+ }
+ }
+}
diff --git a/TechTest/AnyCompany/Repository/UnitOfWork.cs b/TechTest/AnyCompany/Repository/UnitOfWork.cs
new file mode 100644
index 0000000..6130a1a
--- /dev/null
+++ b/TechTest/AnyCompany/Repository/UnitOfWork.cs
@@ -0,0 +1,73 @@
+using AnyCompany.Context;
+using AnyCompany.Entity;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AnyCompany.Repository
+{
+ public class UnitOfWork : IUnitOfWork, IDisposable
+ {
+ private readonly CustomerDbContext customerDbContext;
+
+ public UnitOfWork(CustomerDbContext dbContext)
+ {
+ customerDbContext = dbContext;
+ }
+
+ public List Customers
+ {
+ get
+ {
+ return customerDbContext.Customers.ToList();
+ }
+ }
+
+ public List Orders
+ {
+ get
+ {
+ return customerDbContext.Orders.ToList();
+ }
+ }
+
+ public List VATs
+ {
+ get
+ {
+ return customerDbContext.Vats.ToList();
+ }
+ }
+
+ public List CustomerOrders
+ {
+ get
+ {
+ // Group orders by customer id
+ var ord = from o in Orders
+ group o by o.CustomerId into orderGroup
+ select new { CustomerId = orderGroup.Key, Orders = orderGroup.ToList() };
+
+ // Create CustomerOrder mapping object
+ var customerOrders = (from c in Customers
+ join o in ord on c.CustomerId equals o.CustomerId into cog
+ from p in cog.DefaultIfEmpty()
+ select new CustomerOrder() { Customer = c, Orders = p == null ? new List() : p.Orders }).ToList();
+
+ // return final list mapping object
+ return customerOrders;
+ }
+ }
+
+ public async void SaveChangesAsync()
+ {
+ await customerDbContext.SaveChangesAsync();
+ }
+
+ public void Dispose()
+ {
+ customerDbContext?.Dispose();
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany/Service/OrderService.cs b/TechTest/AnyCompany/Service/OrderService.cs
new file mode 100644
index 0000000..8612bc0
--- /dev/null
+++ b/TechTest/AnyCompany/Service/OrderService.cs
@@ -0,0 +1,53 @@
+using AnyCompany.Entity;
+using AnyCompany.Repository;
+using System.Linq;
+
+namespace AnyCompany.Service
+{
+ public class OrderService
+ {
+ private readonly OrderRepository orderRepository;
+ private readonly IUnitOfWork unitOfWork;
+
+ public OrderService(IUnitOfWork UnitOfwork)
+ {
+ unitOfWork = UnitOfwork;
+ orderRepository = new OrderRepository(UnitOfwork);
+ }
+
+ public bool PlaceOrder(Order order)
+ {
+ bool orderPlaced = false;
+
+ if (IsValidOrder(order))
+ {
+ try
+ {
+ Customer customer = CustomerRepository.Load(order.CustomerId, unitOfWork);
+
+ var applicableVat = unitOfWork.VATs.SingleOrDefault(s => string.Compare(s.Country, customer.Country, true) == 0);
+
+ if (applicableVat != null)
+ order.VAT = applicableVat.ApplyVat;
+ else
+ order.VAT = 0;
+
+ orderRepository.Save(order);
+
+ orderPlaced = true;
+ }
+ catch
+ {
+ //Logging and exception handling
+ }
+ }
+
+ return orderPlaced;
+ }
+
+ public bool IsValidOrder(Order order)
+ {
+ return order.Amount > 0;
+ }
+ }
+}
diff --git a/TechTest/AnyCompany/packages.config b/TechTest/AnyCompany/packages.config
new file mode 100644
index 0000000..b3daf0d
--- /dev/null
+++ b/TechTest/AnyCompany/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/TechTest.sln b/TechTest/TechTest.sln
index 1c1c57a..ad24ebd 100644
--- a/TechTest/TechTest.sln
+++ b/TechTest/TechTest.sln
@@ -5,13 +5,14 @@ VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany", "AnyCompany\AnyCompany.csproj", "{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany.Tests", "AnyCompany.Tests\AnyCompany.Tests.csproj", "{CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B6D3C1BB-2A37-4E17-9EE3-DEF28286E782}"
ProjectSection(SolutionItems) = preProject
Instructions.txt = Instructions.txt
+ ..\Solution.txt = ..\Solution.txt
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany.Tests", "AnyCompany.Tests\AnyCompany.Tests.csproj", "{1983942E-3221-4387-AE22-28B57C38ECCA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -22,10 +23,10 @@ Global
{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}.Release|Any CPU.Build.0 = Release|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1983942E-3221-4387-AE22-28B57C38ECCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1983942E-3221-4387-AE22-28B57C38ECCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1983942E-3221-4387-AE22-28B57C38ECCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1983942E-3221-4387-AE22-28B57C38ECCA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE