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\Stored Procedures\spUserLookup.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>
<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\HomeController.cs" />
<Compile Include="Controllers\ProductController.cs" />
<Compile Include="Controllers\SaleController.cs" />
<Compile Include="Controllers\UserController.cs" />
<Compile Include="Controllers\ValuesController.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" />
</connectionStrings>
<appSettings>
<add key="taxRate" value="8.75" />
</appSettings>
<system.web>
<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;
}
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" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConfigHelper.cs" />
<Compile Include="DataAcccess\ProductData.cs" />
<Compile Include="DataAcccess\SaleData.cs" />
<Compile Include="DataAcccess\UserData.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="Properties\AssemblyInfo.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\IAPIHelper.cs" />
<Compile Include="Api\IProductEndPoint.cs" />
<Compile Include="Api\ISaleEndPoint.cs" />
<Compile Include="Api\ProductEndPoint.cs" />
<Compile Include="Api\SaleEndPoint.cs" />
<Compile Include="Helpers\ConfigHelper.cs" />
<Compile Include="Helpers\IConfigHelper.cs" />
<Compile Include="Models\AuthenticatedUser.cs" />
@ -59,6 +61,8 @@
<Compile Include="Models\ILoggedInUserModel.cs" />
<Compile Include="Models\LoggedInUserModel.cs" />
<Compile Include="Models\ProductModel.cs" />
<Compile Include="Models\SaleDetailModel.cs" />
<Compile Include="Models\SaleModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

View File

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

View File

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

View File

@ -15,10 +15,13 @@ namespace RMWPFUserInterface.ViewModels
{
IProductEndPoint _productEndPoint;
IConfigHelper _configHelper;
public SalesViewModel(IProductEndPoint productEndPoint, IConfigHelper configHelper)
ISaleEndPoint _saleEndPoint;
public SalesViewModel(IProductEndPoint productEndPoint, IConfigHelper configHelper, ISaleEndPoint saleEndPoint)
{
_productEndPoint = productEndPoint;
_configHelper = configHelper;
_saleEndPoint = saleEndPoint;
}
protected override async void OnViewLoaded(object view)
@ -170,6 +173,7 @@ namespace RMWPFUserInterface.ViewModels
NotifyOfPropertyChange(() => SubTotal);
NotifyOfPropertyChange(() => Tax);
NotifyOfPropertyChange(() => Total);
NotifyOfPropertyChange(() => CanCheckOut);
Cart.ResetBindings();
}
@ -189,6 +193,7 @@ namespace RMWPFUserInterface.ViewModels
NotifyOfPropertyChange(() => SubTotal);
NotifyOfPropertyChange(() => Tax);
NotifyOfPropertyChange(() => Total);
NotifyOfPropertyChange(() => CanCheckOut);
}
public bool CanCheckOut
@ -197,14 +202,29 @@ namespace RMWPFUserInterface.ViewModels
{
bool output = false;
// Make sure there is something in the cart
if ( Cart.Count > 0)
{
output = true;
}
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"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5 0 0 0 ">
<TextBlock Text="Quantity :" FontSize="14"/>
<TextBlock Text="Quantity : " FontSize="14"/>
<TextBlock Text="{ Binding QuantityInStock}" FontSize="14"/>
</StackPanel>
</StackPanel>