diff --git a/TechTest/AnyCompany/AnyCompany.csproj b/TechTest/AnyCompany/AnyCompany.csproj index 5b0498d..fcb208e 100644 --- a/TechTest/AnyCompany/AnyCompany.csproj +++ b/TechTest/AnyCompany/AnyCompany.csproj @@ -40,12 +40,25 @@ - - - - - + + + + True + True + Settings.settings + + + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + \ 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/Helpers/VatHelper.cs b/TechTest/AnyCompany/Helpers/VatHelper.cs new file mode 100644 index 0000000..d473d5c --- /dev/null +++ b/TechTest/AnyCompany/Helpers/VatHelper.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Specialized; + +namespace AnyCompany.Helpers +{ + /// + /// This class contains VAT helper functions. + /// + public static class VatHelper + { + + /// + /// Finds the correct VAT rate in settings based on the given country. + /// + /// The country code to use for the VAT lookup. + public static double GetVatRateByCountry(string countryCode) + { + StringCollection vatSettings = Properties.Settings.Default.VatSettings; + + foreach (string setting in vatSettings) + { + // Confirm that this settings matches the 'CountryCode:Rate' pattern, split into an array, and confirm the second array element is indeed double compatible. + if (setting.Contains(":")) + { + string[] settingsPart = setting.Split(':'); + + if (settingsPart.Length == 2 && settingsPart[0].Equals(countryCode, StringComparison.OrdinalIgnoreCase)) + { + double testVal = 0; + + if (double.TryParse(settingsPart[1], out testVal)) + { + return testVal; + } + } + } + } + + return 0; + } + } +} diff --git a/TechTest/AnyCompany/Models/Customer.cs b/TechTest/AnyCompany/Models/Customer.cs new file mode 100644 index 0000000..a7cadc3 --- /dev/null +++ b/TechTest/AnyCompany/Models/Customer.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace AnyCompany.Models +{ + /// + /// This is the Customer Model + /// + public class Customer + { + public int CustomerId { get; set; } + + public string Country { get; set; } + + public DateTime DateOfBirth { get; set; } + + public string Name { get; set; } + + public List Orders { get; set; } + + public Customer() + { + Orders = new List(); + } + } +} diff --git a/TechTest/AnyCompany/Order.cs b/TechTest/AnyCompany/Models/Order.cs similarity index 52% rename from TechTest/AnyCompany/Order.cs rename to TechTest/AnyCompany/Models/Order.cs index fec8e7b..d48ae45 100644 --- a/TechTest/AnyCompany/Order.cs +++ b/TechTest/AnyCompany/Models/Order.cs @@ -1,9 +1,14 @@ -namespace AnyCompany +namespace AnyCompany.Models { + /// + /// This is the Order Model + /// public class Order { public int OrderId { get; set; } public double Amount { get; set; } public double VAT { get; set; } + + public int CustomerId { 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/Settings.Designer.cs b/TechTest/AnyCompany/Properties/Settings.Designer.cs new file mode 100644 index 0000000..e563077 --- /dev/null +++ b/TechTest/AnyCompany/Properties/Settings.Designer.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AnyCompany.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Data Source=(local);Database=Orders;User Id=admin;Password=password;")] + public string OrderConnectionString { + get { + return ((string)(this["OrderConnectionString"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Data Source=(local);Database=Customers;User Id=admin;Password=password;")] + public string CustomerConnectionString { + get { + return ((string)(this["CustomerConnectionString"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\r\n\r\n UK: 0.2\r\n")] + public global::System.Collections.Specialized.StringCollection VatSettings { + get { + return ((global::System.Collections.Specialized.StringCollection)(this["VatSettings"])); + } + } + } +} diff --git a/TechTest/AnyCompany/Properties/Settings.settings b/TechTest/AnyCompany/Properties/Settings.settings new file mode 100644 index 0000000..715ce80 --- /dev/null +++ b/TechTest/AnyCompany/Properties/Settings.settings @@ -0,0 +1,18 @@ + + + + + + Data Source=(local);Database=Orders;User Id=admin;Password=password; + + + Data Source=(local);Database=Customers;User Id=admin;Password=password; + + + <?xml version="1.0" encoding="utf-16"?> +<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + <string>UK:0.2</string> +</ArrayOfString> + + + \ No newline at end of file diff --git a/TechTest/AnyCompany/Repositories/CustomerRepository.cs b/TechTest/AnyCompany/Repositories/CustomerRepository.cs new file mode 100644 index 0000000..dc275b2 --- /dev/null +++ b/TechTest/AnyCompany/Repositories/CustomerRepository.cs @@ -0,0 +1,70 @@ +using AnyCompany.Models; +using System; +using System.Collections.Generic; +using System.Data.SqlClient; + +namespace AnyCompany.Repositories +{ + /// + /// This is the Customer Repository, responsible for managing customer persistance and retrieval. + /// + public static class CustomerRepository + { + /// + /// This loads a Customer object. + /// + /// The Id of the customer this order is placed agains. + public static Customer Load(int customerId) + { + Customer customer = new Customer(); + + SqlConnection connection = new SqlConnection(Properties.Settings.Default.CustomerConnectionString); + connection.Open(); + + SqlCommand command = new SqlCommand("SELECT * FROM Customer WHERE CustomerId = @CustomerId", connection); + + command.Parameters.AddWithValue("@CustomerId", customerId); + + var reader = command.ExecuteReader(); + + while (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(); + } + + connection.Close(); + + return customer; + } + + public static List GetAll() + { + List customers = new List(); + + SqlConnection connection = new SqlConnection(Properties.Settings.Default.CustomerConnectionString); + connection.Open(); + + SqlCommand command = new SqlCommand("SELECT * FROM Customer", connection); + + var reader = command.ExecuteReader(); + + while (reader.Read()) + { + customers.Add(new Customer() + { + CustomerId = int.Parse(reader["CustomerId"].ToString()), + Name = reader["Name"].ToString(), + DateOfBirth = DateTime.Parse(reader["DateOfBirth"].ToString()), + Country = reader["Country"].ToString() + }); + } + + connection.Close(); + + return customers; + } + } +} diff --git a/TechTest/AnyCompany/Repositories/OrderRepository.cs b/TechTest/AnyCompany/Repositories/OrderRepository.cs new file mode 100644 index 0000000..b1a498b --- /dev/null +++ b/TechTest/AnyCompany/Repositories/OrderRepository.cs @@ -0,0 +1,62 @@ +using AnyCompany.Models; +using System.Collections.Generic; +using System.Data.SqlClient; + +namespace AnyCompany.Repositories +{ + /// + /// This is the OrderRepository Repository, responsible for managing customer persistance and retrieval. + /// + internal class OrderRepository + { + /// + /// This stores an Order object. + /// + /// The order information. + public void Save(Order order) + { + SqlConnection connection = new SqlConnection(Properties.Settings.Default.OrderConnectionString); + 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 List GetAllByCustomerId(int customerId) + { + List orders = new List(); + + SqlConnection connection = new SqlConnection(Properties.Settings.Default.CustomerConnectionString); + connection.Open(); + + SqlCommand command = new SqlCommand("SELECT * FROM Orders WHERE CustomerId = @CustomerId", connection); + + command.Parameters.AddWithValue("@CustomerId", customerId); + + var reader = command.ExecuteReader(); + + while (reader.Read()) + { + orders.Add(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()) + }); + } + + connection.Close(); + + return orders; + } + } +} diff --git a/TechTest/AnyCompany/Services/OrderService.cs b/TechTest/AnyCompany/Services/OrderService.cs new file mode 100644 index 0000000..3154455 --- /dev/null +++ b/TechTest/AnyCompany/Services/OrderService.cs @@ -0,0 +1,56 @@ +using AnyCompany.Helpers; +using AnyCompany.Models; +using AnyCompany.Repositories; +using System; +using System.Collections.Generic; + +namespace AnyCompany.Services +{ + /// + /// This is the Order Service, and main entry point for this library. Orders are managed from here. + /// + public class OrderService + { + private readonly OrderRepository orderRepository = new OrderRepository(); + + /// + /// This places and persists an order. + /// + /// The order information. + /// The Id of the customer this order is placed agains. + public bool PlaceOrder(Order order, int customerId) + { + Customer customer = CustomerRepository.Load(customerId); + + if (customer == null) + throw new ArgumentException("Customer could not be loaded. Invalid customer Id."); + + if (order.Amount == 0) + return false; + + order.VAT = VatHelper.GetVatRateByCountry(customer.Country); + order.CustomerId = customer.CustomerId; + orderRepository.Save(order); + + return true; + } + + public List ListCustomers(bool includeOrders) + { + // Fetch all customers from the customer database + List customers = CustomerRepository.GetAll(); + + // Now, fetch all orders for each customer from the orders database + // This method is slow due to a unique call per customer. A future update should perhaps investigate passing a list of customers, and splitting in memory after retrieval. + if (includeOrders) + { + foreach (Customer customer in customers) + { + customer.Orders = orderRepository.GetAllByCustomerId(customer.CustomerId); + } + } + + return customers; + } + } +} diff --git a/TechTest/AnyCompany/app.config b/TechTest/AnyCompany/app.config new file mode 100644 index 0000000..1f98457 --- /dev/null +++ b/TechTest/AnyCompany/app.config @@ -0,0 +1,26 @@ + + + + +
+ + + + + + Data Source=(local);Database=Orders;User Id=admin;Password=password; + + + Data Source=(local);Database=Customers;User Id=admin;Password=password; + + + + + UK: 0.2 + + + + + + \ No newline at end of file