Created Sales View and Database prodecures for displaying items.

Created and binded a View, created logic behind Cart Product Model
This commit is contained in:
s459315 2022-07-23 17:09:13 +02:00
parent ff6653c1bd
commit 57d2ab60a7
23 changed files with 324 additions and 39 deletions

View File

@ -72,6 +72,7 @@
<Build Include="dbo\Tables\Product.sql" /> <Build Include="dbo\Tables\Product.sql" />
<Build Include="dbo\Tables\Inventory.sql" /> <Build Include="dbo\Tables\Inventory.sql" />
<Build Include="dbo\Stored Procedures\spUserLookup.sql" /> <Build Include="dbo\Stored Procedures\spUserLookup.sql" />
<Build Include="dbo\Stored Procedures\spProductGetAll.sql" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<RefactorLog Include="RMData.refactorlog" /> <RefactorLog Include="RMData.refactorlog" />

View File

@ -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

View File

@ -4,7 +4,7 @@
[ProductName] NVARCHAR(100) NOT NULL, [ProductName] NVARCHAR(100) NOT NULL,
[Description] NVARCHAR(MAX) NOT NULL, [Description] NVARCHAR(MAX) NOT NULL,
[RetailPrice] MONEY NOT NULL, [RetailPrice] MONEY NOT NULL,
[QuantityInStock] INT NOT NULL DEFAULT 1,
[CreatedDate] DATETIME2 NOT NULL DEFAULT getutcdate(), [CreatedDate] DATETIME2 NOT NULL DEFAULT getutcdate(),
[LastModified] DATETIME2 NOT NULL DEFAULT getutcdate(), [LastModified] DATETIME2 NOT NULL DEFAULT getutcdate()
) )

View File

@ -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<ProductModel> Get()
{
ProductData data = new ProductData();
return data.GetProducts();
}
}
}

View File

@ -213,6 +213,7 @@
<Compile Include="Areas\HelpPage\XmlDocumentationProvider.cs" /> <Compile Include="Areas\HelpPage\XmlDocumentationProvider.cs" />
<Compile Include="Controllers\AccountController.cs" /> <Compile Include="Controllers\AccountController.cs" />
<Compile Include="Controllers\HomeController.cs" /> <Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\ProductController.cs" />
<Compile Include="Controllers\UserController.cs" /> <Compile Include="Controllers\UserController.cs" />
<Compile Include="Controllers\ValuesController.cs" /> <Compile Include="Controllers\ValuesController.cs" />
<Compile Include="Global.asax.cs"> <Compile Include="Global.asax.cs">

View File

@ -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<ProductModel> GetProducts()
{
SqlDataAccess sql = new SqlDataAccess();
var output = sql.LoadData<ProductModel, dynamic>("dbo.spProductGetAll", new { }, "RMData");
return output;
}
}
}

View File

@ -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; }
}
}

View File

@ -45,7 +45,9 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DataAcccess\ProductData.cs" />
<Compile Include="DataAcccess\UserData.cs" /> <Compile Include="DataAcccess\UserData.cs" />
<Compile Include="Models\ProductModel.cs" />
<Compile Include="Models\UserModel.cs" /> <Compile Include="Models\UserModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Internal\DataAccess\SqlDataAccess.cs" /> <Compile Include="Internal\DataAccess\SqlDataAccess.cs" />

View File

@ -13,21 +13,30 @@ namespace RMWPFInterfaceLibrary.Api
{ {
public class APIHelper : IAPIHelper public class APIHelper : IAPIHelper
{ {
private HttpClient apiClient; private HttpClient _apiClient;
private ILoggedInUserModel _loggedInUser; private ILoggedInUserModel _loggedInUser;
public APIHelper(ILoggedInUserModel loggedInUser) public APIHelper(ILoggedInUserModel loggedInUser)
{ {
InitializeClient(); InitializeClient();
_loggedInUser = loggedInUser; _loggedInUser = loggedInUser;
} }
public HttpClient ApiClient
{
get
{
return _apiClient;
}
}
private void InitializeClient() private void InitializeClient()
{ {
string api = ConfigurationManager.AppSettings["api"]; string api = ConfigurationManager.AppSettings["api"];
apiClient = new HttpClient(); _apiClient = new HttpClient();
apiClient.BaseAddress = new Uri(api); _apiClient.BaseAddress = new Uri(api);
apiClient.DefaultRequestHeaders.Accept.Clear(); _apiClient.DefaultRequestHeaders.Accept.Clear();
apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
} }
public async Task<AuthenticatedUser> Authenticate(string username, string password) public async Task<AuthenticatedUser> Authenticate(string username, string password)
@ -39,7 +48,7 @@ namespace RMWPFInterfaceLibrary.Api
new KeyValuePair<string, string>("password", password), new KeyValuePair<string, string>("password", password),
}); });
using (HttpResponseMessage response = await apiClient.PostAsync("/Token", data)) using (HttpResponseMessage response = await _apiClient.PostAsync("/Token", data))
{ {
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
@ -55,12 +64,12 @@ namespace RMWPFInterfaceLibrary.Api
public async Task GetLogedInUserInfo(string token) public async Task GetLogedInUserInfo(string token)
{ {
apiClient.DefaultRequestHeaders.Clear(); _apiClient.DefaultRequestHeaders.Clear();
apiClient.DefaultRequestHeaders.Accept.Clear(); _apiClient.DefaultRequestHeaders.Accept.Clear();
apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
apiClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); _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) if (response.IsSuccessStatusCode)
{ {

View File

@ -1,10 +1,12 @@
using RMWPFUserInterface.Models; using RMWPFUserInterface.Models;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace RMWPFInterfaceLibrary.Api namespace RMWPFInterfaceLibrary.Api
{ {
public interface IAPIHelper public interface IAPIHelper
{ {
HttpClient ApiClient { get; }
Task<AuthenticatedUser> Authenticate(string username, string password); Task<AuthenticatedUser> Authenticate(string username, string password);
Task GetLogedInUserInfo(string token); Task GetLogedInUserInfo(string token);
} }

View File

@ -0,0 +1,11 @@
using RMWPFInterfaceLibrary.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace RMWPFInterfaceLibrary.Api
{
public interface IProductEndPoint
{
Task<List<ProductModel>> GetAll();
}
}

View File

@ -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<List<ProductModel>> GetAll()
{
using (HttpResponseMessage response = await _apiHelper.ApiClient.GetAsync("api/Product"))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<List<ProductModel>>();
return result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}
}

View File

@ -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})";
}
}
}
}

View File

@ -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; }
}
}

View File

@ -50,9 +50,13 @@
<ItemGroup> <ItemGroup>
<Compile Include="Api\APIHelper.cs" /> <Compile Include="Api\APIHelper.cs" />
<Compile Include="Api\IAPIHelper.cs" /> <Compile Include="Api\IAPIHelper.cs" />
<Compile Include="Api\IProductEndPoint.cs" />
<Compile Include="Api\ProductEndPoint.cs" />
<Compile Include="Models\AuthenticatedUser.cs" /> <Compile Include="Models\AuthenticatedUser.cs" />
<Compile Include="Models\CartItemModel.cs" />
<Compile Include="Models\ILoggedInUserModel.cs" /> <Compile Include="Models\ILoggedInUserModel.cs" />
<Compile Include="Models\LoggedInUserModel.cs" /> <Compile Include="Models\LoggedInUserModel.cs" />
<Compile Include="Models\ProductModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -27,7 +27,8 @@ namespace RMWPFUserInterface
} }
protected override void Configure() protected override void Configure()
{ {
_container.Instance(_container); _container.Instance(_container)
.PerRequest<IProductEndPoint, ProductEndPoint>();
_container _container
.Singleton<IWindowManager, WindowManager>() .Singleton<IWindowManager, WindowManager>()

View File

@ -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
{
}
}

View File

@ -81,6 +81,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="BootStrapper.cs" /> <Compile Include="BootStrapper.cs" />
<Compile Include="EventModels\LogOnEvent.cs" />
<Compile Include="Helpers\PasswordBoxHelper.cs" /> <Compile Include="Helpers\PasswordBoxHelper.cs" />
<Compile Include="Models\AuthenticatedUser.cs" /> <Compile Include="Models\AuthenticatedUser.cs" />
<Compile Include="ViewModels\LoginViewModel.cs" /> <Compile Include="ViewModels\LoginViewModel.cs" />

View File

@ -1,5 +1,6 @@
using Caliburn.Micro; using Caliburn.Micro;
using RMWPFInterfaceLibrary.Api; using RMWPFInterfaceLibrary.Api;
using RMWPFUserInterface.EventModels;
using RMWPFUserInterface.Helpers; using RMWPFUserInterface.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -14,10 +15,12 @@ namespace RMWPFUserInterface.ViewModels
private string _userName; private string _userName;
private string _password; private string _password;
private IAPIHelper _apiHelper; private IAPIHelper _apiHelper;
private IEventAggregator _events;
public LoginViewModel(IAPIHelper apiHelper) public LoginViewModel(IAPIHelper apiHelper, IEventAggregator events)
{ {
_apiHelper = apiHelper; _apiHelper = apiHelper;
_events = events;
} }
public string UserName public string UserName
{ {
@ -96,6 +99,8 @@ namespace RMWPFUserInterface.ViewModels
// Capture more information // Capture more information
await _apiHelper.GetLogedInUserInfo(result.Access_Token); await _apiHelper.GetLogedInUserInfo(result.Access_Token);
await _events.PublishOnUIThreadAsync(new LogOnEvent());
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -1,4 +1,6 @@
using Caliburn.Micro; using Caliburn.Micro;
using RMWPFInterfaceLibrary.Api;
using RMWPFInterfaceLibrary.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -10,9 +12,27 @@ namespace RMWPFUserInterface.ViewModels
{ {
public class SalesViewModel : Screen public class SalesViewModel : Screen
{ {
private BindingList<string> _products; IProductEndPoint _productEndPoint;
public SalesViewModel(IProductEndPoint productEndPoint)
{
_productEndPoint = productEndPoint;
}
public BindingList<string> 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<ProductModel>(prods);
}
private BindingList<ProductModel> _products;
public BindingList<ProductModel> Products
{ {
get { return _products; } get { return _products; }
set set
@ -22,27 +42,42 @@ namespace RMWPFUserInterface.ViewModels
} }
} }
private BindingList<string> _cart; private ProductModel _selectedProduct;
public BindingList<string> Cart public ProductModel SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
NotifyOfPropertyChange(() => SelectedProduct);
NotifyOfPropertyChange(() => CanAddToCart);
}
}
private BindingList<CartItemModel> _cart = new BindingList<CartItemModel>();
public BindingList<CartItemModel> Cart
{ {
get { return _cart; } get { return _cart; }
set set
{ {
Cart = value; _cart = value;
NotifyOfPropertyChange(() => Cart); NotifyOfPropertyChange(() => Cart);
} }
} }
private string _itemQuantity; private int _itemQuantity = 1;
public string ItemQuantity public int ItemQuantity
{ {
get { return _itemQuantity; } get { return _itemQuantity; }
set set
{ {
ItemQuantity = value; _itemQuantity = value;
NotifyOfPropertyChange(() => ItemQuantity); NotifyOfPropertyChange(() => ItemQuantity);
NotifyOfPropertyChange(() => CanAddToCart);
} }
} }
@ -50,8 +85,14 @@ namespace RMWPFUserInterface.ViewModels
{ {
get get
{ {
// replace with calulation; decimal subTotal = 0;
return "$0.00";
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; bool output = false;
// Make sure item is selected and quantity is typed in if (ItemQuantity > 0 && SelectedProduct?.QuantityInStock >= ItemQuantity)
{
output = true;
}
return output; return output;
} }
} }
public void AddToCart() 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 public bool CanRemoveFromCart
@ -102,7 +165,7 @@ namespace RMWPFUserInterface.ViewModels
} }
public void RemoveFromCart() public void RemoveFromCart()
{ {
NotifyOfPropertyChange(() => SubTotal);
} }
public bool CanCheckOut public bool CanCheckOut

View File

@ -2,18 +2,35 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Caliburn.Micro; using Caliburn.Micro;
using RMWPFUserInterface.EventModels;
namespace RMWPFUserInterface.ViewModels namespace RMWPFUserInterface.ViewModels
{ {
public class ShellViewModel : Conductor<Object> public class ShellViewModel : Conductor<Object>, IHandle<LogOnEvent>
{ {
private LoginViewModel _loginVM; private IEventAggregator _events;
public ShellViewModel(LoginViewModel loginVM) private SalesViewModel _salesVM;
private SimpleContainer _container;
public ShellViewModel(IEventAggregator events, SalesViewModel salesVM,
SimpleContainer container)
{ {
_loginVM = loginVM; _salesVM = salesVM;
ActivateItemAsync(_loginVM); _container = container;
_events = events;
_events.SubscribeOnUIThread(this);
ActivateItemAsync(_container.GetInstance<LoginViewModel>());
}
public Task HandleAsync(LogOnEvent message, CancellationToken cancellationToken)
{
ActivateItemAsync(_salesVM);
return Task.CompletedTask;
} }
} }
} }

View File

@ -32,13 +32,19 @@
<!-- Column 0 --> <!-- Column 0 -->
<TextBlock Text="Items" Grid.Row="1" Grid.Column="0"/> <TextBlock Text="Items" Grid.Row="1" Grid.Column="0"/>
<ListBox x:Name="Products" Grid.Row="2" Grid.Column="0" <ListBox x:Name="Products" Grid.Row="2" Grid.Column="0"
MinHeight="200" MinWidth="150"/> MinHeight="200" MinWidth="150" SelectedItem="{Binding SelectedProduct}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProductName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Column 1 --> <!-- Column 1 -->
<StackPanel Orientation="Vertical" Grid.Column="1" <StackPanel Orientation="Vertical" Grid.Column="1"
Grid.Row="2" Margin="20 0" > Grid.Row="2" Margin="20 0" >
<TextBlock Text="Quantity" /> <TextBlock Text="Quantity" />
<TextBlock x:Name="ItemQuantity" MinWidth="100" Margin="0 0 0 10"/> <TextBox x:Name="ItemQuantity" MinWidth="100" Margin="0 0 0 10"/>
<Button x:Name="AddToCart" Content="Add to Cart" Margin="0 0 0 30" <Button x:Name="AddToCart" Content="Add to Cart" Margin="0 0 0 30"
Padding="5"/> Padding="5"/>
<Button x:Name="RemoveFromCart" Content="Remove from Cart" <Button x:Name="RemoveFromCart" Content="Remove from Cart"
@ -48,7 +54,13 @@
<!-- Column 2 --> <!-- Column 2 -->
<TextBlock Text="Cart" Grid.Row="1" Grid.Column="2"/> <TextBlock Text="Cart" Grid.Row="1" Grid.Column="2"/>
<ListBox x:Name="Cart" Grid.Row="2" Grid.Column="2" <ListBox x:Name="Cart" Grid.Row="2" Grid.Column="2"
MinHeight="200" MinWidth="150"/> MinHeight="200" MinWidth="150">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayText}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DockPanel Grid.Row="3" Grid.Column="2"> <DockPanel Grid.Row="3" Grid.Column="2">
<TextBlock Text="SubTotal: " Margin="0 0 15 0"/> <TextBlock Text="SubTotal: " Margin="0 0 15 0"/>

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RMWPFUserInterface.Views" xmlns:local="clr-namespace:RMWPFUserInterface.Views"
mc:Ignorable="d" mc:Ignorable="d"
Title="ShellView" Height="450" Width="800"> Title="ShellView" Height="600" Width="1000">
<DockPanel> <DockPanel>
<Menu DockPanel.Dock="Top" FontSize="18"> <Menu DockPanel.Dock="Top" FontSize="18">
<MenuItem Header="_File"> <MenuItem Header="_File">
@ -16,7 +16,7 @@
</MenuItem> </MenuItem>
</Menu> </Menu>
<Grid> <Grid>
<ContentControl x:Name="ActiveItem" Margin="5" /> <ContentControl x:Name="ActiveItem" Margin="20 5 20 20" />
</Grid> </Grid>
</DockPanel> </DockPanel>
</Window> </Window>