Add Saving Cart to the DB

All the changes necessary to save shopping cart to DB
This commit is contained in:
s459315 2022-07-25 17:02:36 +02:00
parent e94a0a4b85
commit f0bea75284
25 changed files with 357 additions and 7 deletions

View File

@ -73,6 +73,10 @@
<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" /> <Build Include="dbo\Stored Procedures\spProductGetAll.sql" />
<Build Include="dbo\Stored Procedures\spProductGetById.sql" />
<Build Include="dbo\Stored Procedures\spSaleInsert.sql" />
<Build Include="dbo\Stored Procedures\spSaleDetailInsert.sql" />
<Build Include="dbo\Stored Procedures\spSaleLookUp.sql" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<RefactorLog Include="RMData.refactorlog" /> <RefactorLog Include="RMData.refactorlog" />

View File

@ -0,0 +1,10 @@
CREATE PROCEDURE [dbo].[spProductGetById]
@Id int
AS
BEGIN
SET NOCOUNT ON;
select Id, ProductName, [Description], RetailPrice, QuantityInStock, IsTaxable
from [dbo].[Product]
where Id = @Id;
END

View File

@ -0,0 +1,14 @@
CREATE PROCEDURE [dbo].[spSaleDetailInsert]
@SaleId int,
@ProductId int,
@Quantity int,
@PurchasePrice money,
@Tax money
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.SaleDetail(SaleId, ProductId, Quantity, PurchasePrice, Tax)
VALUES (@SaleId, @ProductId, @Quantity, @PurchasePrice, @Tax)
END

View File

@ -0,0 +1,16 @@
CREATE PROCEDURE [dbo].[spSaleInsert]
@Id int output,
@CashierId nvarchar(128),
@SaleDate datetime2,
@SubTotal money,
@Tax money,
@Total money
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.Sale(CashierId, SaleDate, SubTotal, Tax, Total)
VALUES (@CashierId, @SaleDate, @SubTotal, @Tax, @Total);
SELECT @Id = @@IDENTITY;
END

View File

@ -0,0 +1,11 @@
CREATE PROCEDURE [dbo].[spSaleLookUp]
@CashierId nvarchar(128),
@SaleDate datetime2
AS
BEGIN
SET NOCOUNT ON;
Select Id
from dbo.Sale
where CashierId = @CashierId and SaleDate = @SaleDate;
END

View File

@ -0,0 +1,24 @@
using Microsoft.AspNet.Identity;
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 SaleController : ApiController
{
public void Post(SaleModel sale)
{
string cashierId = RequestContext.Principal.Identity.GetUserId();
SaleData data = new SaleData();
data.SaveSale(sale, cashierId);
}
}
}

View File

@ -214,6 +214,7 @@
<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\ProductController.cs" />
<Compile Include="Controllers\SaleController.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

@ -13,6 +13,7 @@
<add name="RMData" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=RMData;Integrated Security=True;Connect Timeout=60;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" /> <add name="RMData" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=RMData;Integrated Security=True;Connect Timeout=60;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" />
</connectionStrings> </connectionStrings>
<appSettings> <appSettings>
<add key="taxRate" value="8.75" />
</appSettings> </appSettings>
<system.web> <system.web>
<authentication mode="None" /> <authentication mode="None" />

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RMDataManagerLibrary
{
public class ConfigHelper
{
public static decimal GetTaxRate()
{
string rateText = ConfigurationManager.AppSettings["taxRate"];
bool isValidTaxRate = Decimal.TryParse(rateText, out decimal output);
if (isValidTaxRate == false)
{
throw new ConfigurationErrorsException("The taxRate is not setup properly");
}
return output;
}
}
}

View File

@ -18,5 +18,14 @@ namespace RMDataManagerLibrary.DataAcccess
return output; return output;
} }
public ProductModel GetProductById(int productId)
{
SqlDataAccess sql = new SqlDataAccess();
var output = sql.LoadData<ProductModel, dynamic>("dbo.spProductGetById", new {Id = productId}, "RMData").FirstOrDefault();
return output;
}
} }
} }

View File

@ -0,0 +1,76 @@
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 SaleData
{
//TODO: Make this SOLID/DRY/BETTER
public void SaveSale(SaleModel saleInfo, string cashierId)
{
List<SaleDetailDBModel> details = new List<SaleDetailDBModel>();
ProductData products = new ProductData();
decimal taxRate = ConfigHelper.GetTaxRate()/100;
foreach (var item in saleInfo.SaleDetails)
{
var detail = new SaleDetailDBModel
{
ProductId = item.ProductId,
Quantity = item.Quantity
};
// get the information about this product
var productInfo = products.GetProductById(item.ProductId);
if (productInfo == null)
{
throw new Exception($"Product with Id {item.ProductId} could not be found in DB");
}
detail.PurchasePrice = productInfo.RetailPrice * detail.Quantity;
if (productInfo.IsTaxable)
{
detail.Tax = detail.PurchasePrice * taxRate;
}
details.Add(detail);
}
SaleDBModel sale = new SaleDBModel
{
SubTotal = details.Sum(x => x.PurchasePrice),
Tax = details.Sum(x => x.Tax),
CashierId = cashierId
};
sale.Total = sale.SubTotal + sale.Tax;
//save
SqlDataAccess sql = new SqlDataAccess();
sql.SaveData("dbo.spSaleInsert", sale, "RMData");
// get id from saleModel
sale.Id = sql.LoadData<int, dynamic>("spSaleLookUp",new { sale.CashierId, sale.SaleDate }, "RMData").FirstOrDefault();
// finish filling up sale details
foreach (var item in details)
{
item.SaleId = sale.Id;
// Save Details
sql.SaveData("dbo.spSaleDetailInsert", item, "RMData");
}
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RMDataManagerLibrary.Models
{
public class SaleDBModel
{
public int Id { get; set; }
public string CashierId { get; set; }
public DateTime SaleDate { get; set; } = DateTime.UtcNow;
public decimal SubTotal { get; set; }
public decimal Tax { get; set; }
public decimal Total { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RMDataManagerLibrary.Models
{
public class SaleDetailDBModel
{
public int SaleId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal PurchasePrice { get; set; }
public decimal Tax { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace RMDataManagerLibrary.Models
{
public class SaleDetailModel
{
public int ProductId { get; set; }
public int Quantity { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace RMDataManagerLibrary.Models
{
public class SaleModel
{
public List<SaleDetailModel> SaleDetails { get; set; }
}
}

View File

@ -45,9 +45,15 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ConfigHelper.cs" />
<Compile Include="DataAcccess\ProductData.cs" /> <Compile Include="DataAcccess\ProductData.cs" />
<Compile Include="DataAcccess\SaleData.cs" />
<Compile Include="DataAcccess\UserData.cs" /> <Compile Include="DataAcccess\UserData.cs" />
<Compile Include="Models\ProductModel.cs" /> <Compile Include="Models\ProductModel.cs" />
<Compile Include="Models\SaleDBModel.cs" />
<Compile Include="Models\SaleDetailDBModel.cs" />
<Compile Include="Models\SaleDetailModel.cs" />
<Compile Include="Models\SaleModel.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

@ -0,0 +1,10 @@
using RMWPFInterfaceLibrary.Models;
using System.Threading.Tasks;
namespace RMWPFInterfaceLibrary.Api
{
public interface ISaleEndPoint
{
Task PostSale(SaleModel sale);
}
}

View File

@ -0,0 +1,34 @@
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 SaleEndPoint : ISaleEndPoint
{
private IAPIHelper _apiHelper;
public SaleEndPoint(IAPIHelper apiHelper)
{
_apiHelper = apiHelper;
}
public async Task PostSale(SaleModel sale)
{
using (HttpResponseMessage response = await _apiHelper.ApiClient.PostAsJsonAsync("api/Sale", sale))
{
if (response.IsSuccessStatusCode)
{
// Log call
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}
}

View File

@ -0,0 +1,8 @@
namespace RMWPFInterfaceLibrary.Models
{
public class SaleDetailModel
{
public int ProductId { get; set; }
public int Quantity { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RMWPFInterfaceLibrary.Models
{
public class SaleModel
{
public List<SaleDetailModel> SaleDetails { get; set; } = new List<SaleDetailModel>();
}
}

View File

@ -51,7 +51,9 @@
<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\IProductEndPoint.cs" />
<Compile Include="Api\ISaleEndPoint.cs" />
<Compile Include="Api\ProductEndPoint.cs" /> <Compile Include="Api\ProductEndPoint.cs" />
<Compile Include="Api\SaleEndPoint.cs" />
<Compile Include="Helpers\ConfigHelper.cs" /> <Compile Include="Helpers\ConfigHelper.cs" />
<Compile Include="Helpers\IConfigHelper.cs" /> <Compile Include="Helpers\IConfigHelper.cs" />
<Compile Include="Models\AuthenticatedUser.cs" /> <Compile Include="Models\AuthenticatedUser.cs" />
@ -59,6 +61,8 @@
<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="Models\ProductModel.cs" />
<Compile Include="Models\SaleDetailModel.cs" />
<Compile Include="Models\SaleModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@ -12,8 +12,8 @@ namespace RMWPFUserInterface.ViewModels
{ {
public class LoginViewModel : Screen public class LoginViewModel : Screen
{ {
private string _userName; private string _userName ="test@test";
private string _password; private string _password = "Test1234.";
private IAPIHelper _apiHelper; private IAPIHelper _apiHelper;
private IEventAggregator _events; private IEventAggregator _events;

View File

@ -15,10 +15,13 @@ namespace RMWPFUserInterface.ViewModels
{ {
IProductEndPoint _productEndPoint; IProductEndPoint _productEndPoint;
IConfigHelper _configHelper; IConfigHelper _configHelper;
public SalesViewModel(IProductEndPoint productEndPoint, IConfigHelper configHelper) ISaleEndPoint _saleEndPoint;
public SalesViewModel(IProductEndPoint productEndPoint, IConfigHelper configHelper, ISaleEndPoint saleEndPoint)
{ {
_productEndPoint = productEndPoint; _productEndPoint = productEndPoint;
_configHelper = configHelper; _configHelper = configHelper;
_saleEndPoint = saleEndPoint;
} }
protected override async void OnViewLoaded(object view) protected override async void OnViewLoaded(object view)
@ -170,6 +173,7 @@ namespace RMWPFUserInterface.ViewModels
NotifyOfPropertyChange(() => SubTotal); NotifyOfPropertyChange(() => SubTotal);
NotifyOfPropertyChange(() => Tax); NotifyOfPropertyChange(() => Tax);
NotifyOfPropertyChange(() => Total); NotifyOfPropertyChange(() => Total);
NotifyOfPropertyChange(() => CanCheckOut);
Cart.ResetBindings(); Cart.ResetBindings();
} }
@ -189,6 +193,7 @@ namespace RMWPFUserInterface.ViewModels
NotifyOfPropertyChange(() => SubTotal); NotifyOfPropertyChange(() => SubTotal);
NotifyOfPropertyChange(() => Tax); NotifyOfPropertyChange(() => Tax);
NotifyOfPropertyChange(() => Total); NotifyOfPropertyChange(() => Total);
NotifyOfPropertyChange(() => CanCheckOut);
} }
public bool CanCheckOut public bool CanCheckOut
@ -197,14 +202,29 @@ namespace RMWPFUserInterface.ViewModels
{ {
bool output = false; bool output = false;
// Make sure there is something in the cart if ( Cart.Count > 0)
{
output = true;
}
return output; return output;
} }
} }
public void CheckOut() public async Task CheckOut()
{ {
SaleModel sale = new SaleModel();
foreach (var item in Cart)
{
sale.SaleDetails.Add(new SaleDetailModel
{
ProductId = item.Product.Id,
Quantity = item.QuantityInCart
});
}
await _saleEndPoint.PostSale(sale);
} }
} }
} }

View File

@ -43,7 +43,7 @@
<TextBlock Text="{ Binding RetailPrice, StringFormat='{}{0:C}'}" FontSize="14"/> <TextBlock Text="{ Binding RetailPrice, StringFormat='{}{0:C}'}" FontSize="14"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Margin="5 0 0 0 "> <StackPanel Orientation="Horizontal" Margin="5 0 0 0 ">
<TextBlock Text="Quantity :" FontSize="14"/> <TextBlock Text="Quantity : " FontSize="14"/>
<TextBlock Text="{ Binding QuantityInStock}" FontSize="14"/> <TextBlock Text="{ Binding QuantityInStock}" FontSize="14"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>