diff --git a/RMData/RMData.sqlproj b/RMData/RMData.sqlproj index 5471387..a8988d0 100644 --- a/RMData/RMData.sqlproj +++ b/RMData/RMData.sqlproj @@ -72,6 +72,7 @@ + diff --git a/RMData/dbo/Stored Procedures/spProductGetAll.sql b/RMData/dbo/Stored Procedures/spProductGetAll.sql new file mode 100644 index 0000000..d0fe2cc --- /dev/null +++ b/RMData/dbo/Stored Procedures/spProductGetAll.sql @@ -0,0 +1,9 @@ +CREATE PROCEDURE [dbo].[spProductGetAll] +AS +BEGIN + SET NOCOUNT ON; + + select Id, ProductName, [Description], RetailPrice, QuantityInStock + from [dbo].[Product] + order by ProductName; +END \ No newline at end of file diff --git a/RMData/dbo/Tables/Product.sql b/RMData/dbo/Tables/Product.sql index 78867e4..61dada0 100644 --- a/RMData/dbo/Tables/Product.sql +++ b/RMData/dbo/Tables/Product.sql @@ -4,7 +4,7 @@ [ProductName] NVARCHAR(100) NOT NULL, [Description] NVARCHAR(MAX) NOT NULL, [RetailPrice] MONEY NOT NULL, + [QuantityInStock] INT NOT NULL DEFAULT 1, [CreatedDate] DATETIME2 NOT NULL DEFAULT getutcdate(), - [LastModified] DATETIME2 NOT NULL DEFAULT getutcdate(), - + [LastModified] DATETIME2 NOT NULL DEFAULT getutcdate() ) diff --git a/RMDataManager/Controllers/ProductController.cs b/RMDataManager/Controllers/ProductController.cs new file mode 100644 index 0000000..c26380c --- /dev/null +++ b/RMDataManager/Controllers/ProductController.cs @@ -0,0 +1,22 @@ +using RMDataManagerLibrary.DataAcccess; +using RMDataManagerLibrary.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +namespace RMDataManager.Controllers +{ + [Authorize] + public class ProductController : ApiController + { + public List Get() + { + ProductData data = new ProductData(); + + return data.GetProducts(); + } + } +} diff --git a/RMDataManager/RMDataManager.csproj b/RMDataManager/RMDataManager.csproj index 0611b47..ca6a8ed 100644 --- a/RMDataManager/RMDataManager.csproj +++ b/RMDataManager/RMDataManager.csproj @@ -213,6 +213,7 @@ + diff --git a/RMDataManagerLibrary/DataAcccess/ProductData.cs b/RMDataManagerLibrary/DataAcccess/ProductData.cs new file mode 100644 index 0000000..db78ae4 --- /dev/null +++ b/RMDataManagerLibrary/DataAcccess/ProductData.cs @@ -0,0 +1,22 @@ +using RMDataManagerLibrary.Internal.DataAccess; +using RMDataManagerLibrary.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RMDataManagerLibrary.DataAcccess +{ + public class ProductData + { + public List GetProducts() + { + SqlDataAccess sql = new SqlDataAccess(); + + var output = sql.LoadData("dbo.spProductGetAll", new { }, "RMData"); + + return output; + } + } +} diff --git a/RMDataManagerLibrary/Models/ProductModel.cs b/RMDataManagerLibrary/Models/ProductModel.cs new file mode 100644 index 0000000..eba19f7 --- /dev/null +++ b/RMDataManagerLibrary/Models/ProductModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace RMDataManagerLibrary.Models +{ + public class ProductModel + { + public int Id { get; set; } + public string ProductName { get; set; } + public string Description { get; set; } + public decimal RetailPrice { get; set; } + public int QuantityInStock { get; set; } + } +} \ No newline at end of file diff --git a/RMDataManagerLibrary/RMDataManagerLibrary.csproj b/RMDataManagerLibrary/RMDataManagerLibrary.csproj index 897e7d5..5ee02f4 100644 --- a/RMDataManagerLibrary/RMDataManagerLibrary.csproj +++ b/RMDataManagerLibrary/RMDataManagerLibrary.csproj @@ -45,7 +45,9 @@ + + diff --git a/RMWPFInterfaceLibrary/Api/APIHelper.cs b/RMWPFInterfaceLibrary/Api/APIHelper.cs index ef15489..1c64c51 100644 --- a/RMWPFInterfaceLibrary/Api/APIHelper.cs +++ b/RMWPFInterfaceLibrary/Api/APIHelper.cs @@ -13,21 +13,30 @@ namespace RMWPFInterfaceLibrary.Api { public class APIHelper : IAPIHelper { - private HttpClient apiClient; + private HttpClient _apiClient; private ILoggedInUserModel _loggedInUser; public APIHelper(ILoggedInUserModel loggedInUser) { InitializeClient(); _loggedInUser = loggedInUser; } + + public HttpClient ApiClient + { + get + { + return _apiClient; + } + } + private void InitializeClient() { string api = ConfigurationManager.AppSettings["api"]; - apiClient = new HttpClient(); - apiClient.BaseAddress = new Uri(api); - apiClient.DefaultRequestHeaders.Accept.Clear(); - apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + _apiClient = new HttpClient(); + _apiClient.BaseAddress = new Uri(api); + _apiClient.DefaultRequestHeaders.Accept.Clear(); + _apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } public async Task Authenticate(string username, string password) @@ -39,7 +48,7 @@ namespace RMWPFInterfaceLibrary.Api new KeyValuePair("password", password), }); - using (HttpResponseMessage response = await apiClient.PostAsync("/Token", data)) + using (HttpResponseMessage response = await _apiClient.PostAsync("/Token", data)) { if (response.IsSuccessStatusCode) { @@ -55,12 +64,12 @@ namespace RMWPFInterfaceLibrary.Api public async Task GetLogedInUserInfo(string token) { - apiClient.DefaultRequestHeaders.Clear(); - apiClient.DefaultRequestHeaders.Accept.Clear(); - apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); + _apiClient.DefaultRequestHeaders.Clear(); + _apiClient.DefaultRequestHeaders.Accept.Clear(); + _apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + _apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); - using (HttpResponseMessage response = await apiClient.GetAsync("api/User")) + using (HttpResponseMessage response = await _apiClient.GetAsync("api/User")) { if (response.IsSuccessStatusCode) { diff --git a/RMWPFInterfaceLibrary/Api/IAPIHelper.cs b/RMWPFInterfaceLibrary/Api/IAPIHelper.cs index 73ef29a..c4c9819 100644 --- a/RMWPFInterfaceLibrary/Api/IAPIHelper.cs +++ b/RMWPFInterfaceLibrary/Api/IAPIHelper.cs @@ -1,10 +1,12 @@ using RMWPFUserInterface.Models; +using System.Net.Http; using System.Threading.Tasks; namespace RMWPFInterfaceLibrary.Api { public interface IAPIHelper { + HttpClient ApiClient { get; } Task Authenticate(string username, string password); Task GetLogedInUserInfo(string token); } diff --git a/RMWPFInterfaceLibrary/Api/IProductEndPoint.cs b/RMWPFInterfaceLibrary/Api/IProductEndPoint.cs new file mode 100644 index 0000000..dfc691e --- /dev/null +++ b/RMWPFInterfaceLibrary/Api/IProductEndPoint.cs @@ -0,0 +1,11 @@ +using RMWPFInterfaceLibrary.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RMWPFInterfaceLibrary.Api +{ + public interface IProductEndPoint + { + Task> GetAll(); + } +} \ No newline at end of file diff --git a/RMWPFInterfaceLibrary/Api/ProductEndPoint.cs b/RMWPFInterfaceLibrary/Api/ProductEndPoint.cs new file mode 100644 index 0000000..fb6082d --- /dev/null +++ b/RMWPFInterfaceLibrary/Api/ProductEndPoint.cs @@ -0,0 +1,36 @@ +using RMWPFInterfaceLibrary.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace RMWPFInterfaceLibrary.Api +{ + public class ProductEndPoint : IProductEndPoint + { + private IAPIHelper _apiHelper; + public ProductEndPoint(IAPIHelper apiHelper) + { + _apiHelper = apiHelper; + } + + public async Task> GetAll() + { + using (HttpResponseMessage response = await _apiHelper.ApiClient.GetAsync("api/Product")) + { + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadAsAsync>(); + return result; + } + else + { + throw new Exception(response.ReasonPhrase); + } + + } + } + } +} diff --git a/RMWPFInterfaceLibrary/Models/CartItemModel.cs b/RMWPFInterfaceLibrary/Models/CartItemModel.cs new file mode 100644 index 0000000..80292a1 --- /dev/null +++ b/RMWPFInterfaceLibrary/Models/CartItemModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RMWPFInterfaceLibrary.Models +{ + public class CartItemModel + { + public ProductModel Product { get; set; } + public int QuantityInCart { get; set; } + + public string DisplayText + { + get + { + return $"{Product.ProductName} ({QuantityInCart})"; + } + } + } +} diff --git a/RMWPFInterfaceLibrary/Models/ProductModel.cs b/RMWPFInterfaceLibrary/Models/ProductModel.cs new file mode 100644 index 0000000..2c26c94 --- /dev/null +++ b/RMWPFInterfaceLibrary/Models/ProductModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RMWPFInterfaceLibrary.Models +{ + public class ProductModel + { + public int Id { get; set; } + public string ProductName { get; set; } + public string Description { get; set; } + public decimal RetailPrice { get; set; } + public int QuantityInStock { get; set; } + } +} diff --git a/RMWPFInterfaceLibrary/RMWPFInterfaceLibrary.csproj b/RMWPFInterfaceLibrary/RMWPFInterfaceLibrary.csproj index 14f9cdb..b64de7e 100644 --- a/RMWPFInterfaceLibrary/RMWPFInterfaceLibrary.csproj +++ b/RMWPFInterfaceLibrary/RMWPFInterfaceLibrary.csproj @@ -50,9 +50,13 @@ + + + + diff --git a/RMWPFUserInterface/BootStrapper.cs b/RMWPFUserInterface/BootStrapper.cs index b62ce16..094cecf 100644 --- a/RMWPFUserInterface/BootStrapper.cs +++ b/RMWPFUserInterface/BootStrapper.cs @@ -27,8 +27,9 @@ namespace RMWPFUserInterface } protected override void Configure() { - _container.Instance(_container); - + _container.Instance(_container) + .PerRequest(); + _container .Singleton() .Singleton() diff --git a/RMWPFUserInterface/EventModels/LogOnEvent.cs b/RMWPFUserInterface/EventModels/LogOnEvent.cs new file mode 100644 index 0000000..0ef5b7e --- /dev/null +++ b/RMWPFUserInterface/EventModels/LogOnEvent.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RMWPFUserInterface.EventModels +{ + public class LogOnEvent + { + } +} diff --git a/RMWPFUserInterface/RMWPFUserInterface.csproj b/RMWPFUserInterface/RMWPFUserInterface.csproj index bff8b6c..9f5201d 100644 --- a/RMWPFUserInterface/RMWPFUserInterface.csproj +++ b/RMWPFUserInterface/RMWPFUserInterface.csproj @@ -81,6 +81,7 @@ Designer + diff --git a/RMWPFUserInterface/ViewModels/LoginViewModel.cs b/RMWPFUserInterface/ViewModels/LoginViewModel.cs index 121b9a2..58b6e16 100644 --- a/RMWPFUserInterface/ViewModels/LoginViewModel.cs +++ b/RMWPFUserInterface/ViewModels/LoginViewModel.cs @@ -1,5 +1,6 @@ using Caliburn.Micro; using RMWPFInterfaceLibrary.Api; +using RMWPFUserInterface.EventModels; using RMWPFUserInterface.Helpers; using System; using System.Collections.Generic; @@ -14,10 +15,12 @@ namespace RMWPFUserInterface.ViewModels private string _userName; private string _password; private IAPIHelper _apiHelper; + private IEventAggregator _events; - public LoginViewModel(IAPIHelper apiHelper) + public LoginViewModel(IAPIHelper apiHelper, IEventAggregator events) { _apiHelper = apiHelper; + _events = events; } public string UserName { @@ -96,6 +99,8 @@ namespace RMWPFUserInterface.ViewModels // Capture more information await _apiHelper.GetLogedInUserInfo(result.Access_Token); + + await _events.PublishOnUIThreadAsync(new LogOnEvent()); } catch (Exception ex) { diff --git a/RMWPFUserInterface/ViewModels/SalesViewModel.cs b/RMWPFUserInterface/ViewModels/SalesViewModel.cs index 7ff3c91..ec14020 100644 --- a/RMWPFUserInterface/ViewModels/SalesViewModel.cs +++ b/RMWPFUserInterface/ViewModels/SalesViewModel.cs @@ -1,4 +1,6 @@ using Caliburn.Micro; +using RMWPFInterfaceLibrary.Api; +using RMWPFInterfaceLibrary.Models; using System; using System.Collections.Generic; using System.ComponentModel; @@ -10,9 +12,27 @@ namespace RMWPFUserInterface.ViewModels { public class SalesViewModel : Screen { - private BindingList _products; + IProductEndPoint _productEndPoint; + public SalesViewModel(IProductEndPoint productEndPoint) + { + _productEndPoint = productEndPoint; + } - public BindingList Products + protected override async void OnViewLoaded(object view) + { + base.OnViewLoaded(view); + await LoadProducts(); + } + + private async Task LoadProducts() + { + var prods = await _productEndPoint.GetAll(); + Products = new BindingList(prods); + } + + private BindingList _products; + + public BindingList Products { get { return _products; } set @@ -22,27 +42,42 @@ namespace RMWPFUserInterface.ViewModels } } - private BindingList _cart; + private ProductModel _selectedProduct; - public BindingList Cart + public ProductModel SelectedProduct + { + get { return _selectedProduct; } + set + { + _selectedProduct = value; + NotifyOfPropertyChange(() => SelectedProduct); + NotifyOfPropertyChange(() => CanAddToCart); + } + } + + + private BindingList _cart = new BindingList(); + + public BindingList Cart { get { return _cart; } set - { - Cart = value; + { + _cart = value; NotifyOfPropertyChange(() => Cart); } } - private string _itemQuantity; + private int _itemQuantity = 1; - public string ItemQuantity + public int ItemQuantity { get { return _itemQuantity; } set { - ItemQuantity = value; + _itemQuantity = value; NotifyOfPropertyChange(() => ItemQuantity); + NotifyOfPropertyChange(() => CanAddToCart); } } @@ -50,8 +85,14 @@ namespace RMWPFUserInterface.ViewModels { get { - // replace with calulation; - return "$0.00"; + decimal subTotal = 0; + + foreach (var item in Cart) + { + subTotal += item.Product.RetailPrice * item.QuantityInCart; + } + + return subTotal.ToString("C"); } } @@ -79,14 +120,36 @@ namespace RMWPFUserInterface.ViewModels { bool output = false; - // Make sure item is selected and quantity is typed in + if (ItemQuantity > 0 && SelectedProduct?.QuantityInStock >= ItemQuantity) + { + output = true; + } return output; } } public void AddToCart() { + CartItemModel existingItem = Cart.FirstOrDefault(x => x.Product == SelectedProduct); + if (existingItem != null) + { + existingItem.QuantityInCart += ItemQuantity; + } + else + { + CartItemModel item = new CartItemModel + { + Product = SelectedProduct, + QuantityInCart = ItemQuantity + }; + + Cart.Add(item); + } + SelectedProduct.QuantityInStock -= ItemQuantity; + ItemQuantity = 1; + NotifyOfPropertyChange(() => SubTotal); + Cart.ResetBindings(); } public bool CanRemoveFromCart @@ -102,7 +165,7 @@ namespace RMWPFUserInterface.ViewModels } public void RemoveFromCart() { - + NotifyOfPropertyChange(() => SubTotal); } public bool CanCheckOut diff --git a/RMWPFUserInterface/ViewModels/ShellViewModel.cs b/RMWPFUserInterface/ViewModels/ShellViewModel.cs index a7c41a3..49d04ec 100644 --- a/RMWPFUserInterface/ViewModels/ShellViewModel.cs +++ b/RMWPFUserInterface/ViewModels/ShellViewModel.cs @@ -2,18 +2,35 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Caliburn.Micro; +using RMWPFUserInterface.EventModels; namespace RMWPFUserInterface.ViewModels { - public class ShellViewModel : Conductor + public class ShellViewModel : Conductor, IHandle { - private LoginViewModel _loginVM; - public ShellViewModel(LoginViewModel loginVM) + private IEventAggregator _events; + private SalesViewModel _salesVM; + private SimpleContainer _container; + public ShellViewModel(IEventAggregator events, SalesViewModel salesVM, + SimpleContainer container) { - _loginVM = loginVM; - ActivateItemAsync(_loginVM); + _salesVM = salesVM; + _container = container; + _events = events; + + _events.SubscribeOnUIThread(this); + + ActivateItemAsync(_container.GetInstance()); + } + + public Task HandleAsync(LogOnEvent message, CancellationToken cancellationToken) + { + ActivateItemAsync(_salesVM); + + return Task.CompletedTask; } } } diff --git a/RMWPFUserInterface/Views/SalesView.xaml b/RMWPFUserInterface/Views/SalesView.xaml index 5a165c0..83ab146 100644 --- a/RMWPFUserInterface/Views/SalesView.xaml +++ b/RMWPFUserInterface/Views/SalesView.xaml @@ -32,13 +32,19 @@ + MinHeight="200" MinWidth="150" SelectedItem="{Binding SelectedProduct}"> + + + + + + - +