diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs index 9471c6e09..952968490 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs @@ -66,8 +66,9 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers ProductId = catalogItem.Id, ProductName = catalogItem.Name, PictureUrl = catalogItem.PictureUri, - UnitPrice = catalogItem.Price, - Quantity = bitem.Quantity + UnitPrice = itemInBasket.UnitPrice, + Quantity = bitem.Quantity, + isDiscounted = false }); } else diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs index 05fed054e..016a46b1f 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs @@ -16,6 +16,7 @@ public int Quantity { get; set; } public string PictureUrl { get; set; } + public bool isDiscounted { get; internal set; } } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs index 90800374d..a06be3aa2 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs @@ -7,5 +7,7 @@ public int ProductId { get; set; } // Catalog item id public int Quantity { get; set; } // Quantity + + public bool isDiscounted { get; set; } // Discount applied } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs index 648a386c8..0f00334c5 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs @@ -60,7 +60,8 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services ProductId = item.Productid, ProductName = item.Productname, Quantity = item.Quantity, - UnitPrice = (decimal)item.Unitprice + UnitPrice = (decimal)item.Unitprice, + isDiscounted = item.Isdiscounted }); } }); @@ -92,7 +93,8 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services Productid = item.ProductId, Productname = item.ProductName, Quantity = item.Quantity, - Unitprice = (double)item.UnitPrice + Unitprice = (double)item.UnitPrice, + Isdiscounted = item.isDiscounted }); } }); diff --git a/src/Services/Basket/Basket.API/Grpc/BasketService.cs b/src/Services/Basket/Basket.API/Grpc/BasketService.cs index 51633b326..556c8a97e 100644 --- a/src/Services/Basket/Basket.API/Grpc/BasketService.cs +++ b/src/Services/Basket/Basket.API/Grpc/BasketService.cs @@ -72,7 +72,8 @@ namespace GrpcBasket Productid = item.ProductId, Productname = item.ProductName, Quantity = item.Quantity, - Unitprice = (double)item.UnitPrice + Unitprice = (double)item.UnitPrice, + Isdiscounted = item.isDiscounted })); return response; @@ -93,7 +94,8 @@ namespace GrpcBasket ProductId = item.Productid, ProductName = item.Productname, Quantity = item.Quantity, - UnitPrice = (decimal)item.Unitprice + UnitPrice = (decimal)item.Unitprice, + isDiscounted = item.Isdiscounted })); return response; diff --git a/src/Services/Basket/Basket.API/Model/BasketItem.cs b/src/Services/Basket/Basket.API/Model/BasketItem.cs index f781d5a60..e0cf26f2d 100644 --- a/src/Services/Basket/Basket.API/Model/BasketItem.cs +++ b/src/Services/Basket/Basket.API/Model/BasketItem.cs @@ -12,6 +12,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model public decimal OldUnitPrice { get; set; } public int Quantity { get; set; } public string PictureUrl { get; set; } + public bool isDiscounted { get; set; } public IEnumerable Validate(ValidationContext validationContext) { var results = new List(); diff --git a/src/Services/Basket/Basket.API/Proto/basket.proto b/src/Services/Basket/Basket.API/Proto/basket.proto index 76ad33912..821c73e5d 100644 --- a/src/Services/Basket/Basket.API/Proto/basket.proto +++ b/src/Services/Basket/Basket.API/Proto/basket.proto @@ -31,4 +31,5 @@ message BasketItemResponse { double oldunitprice = 5; int32 quantity = 6; string pictureurl = 7; + bool isdiscounted = 8; } diff --git a/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs b/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs index bdbf8afd4..212d7d66d 100644 --- a/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs +++ b/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs @@ -17,6 +17,7 @@ namespace UnitTest.Basket.Application private readonly Mock _catalogServiceMock; private readonly Mock _basketServiceMock; private readonly Mock> _identityParserMock; + private readonly Mock _couponServiceMock; private readonly Mock _contextMock; public CartControllerTest() @@ -24,6 +25,7 @@ namespace UnitTest.Basket.Application _catalogServiceMock = new Mock(); _basketServiceMock = new Mock(); _identityParserMock = new Mock>(); + _couponServiceMock = new Mock; _contextMock = new Mock(); } @@ -33,6 +35,7 @@ namespace UnitTest.Basket.Application //Arrange var fakeBuyerId = "1"; var action = string.Empty; + var couponCode = string.Empty; var fakeBasket = GetFakeBasket(fakeBuyerId); var fakeQuantities = new Dictionary() { @@ -47,9 +50,9 @@ namespace UnitTest.Basket.Application .Returns(Task.FromResult(fakeBasket)); //Act - var cartController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); + var cartController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object, _couponServiceMock.Object); cartController.ControllerContext.HttpContext = _contextMock.Object; - var actionResult = await cartController.Index(fakeQuantities, action); + var actionResult = await cartController.Index(fakeQuantities, couponCode, action); //Assert var viewResult = Assert.IsType(actionResult); @@ -61,6 +64,7 @@ namespace UnitTest.Basket.Application //Arrange var fakeBuyerId = "1"; var action = "[ Checkout ]"; + var couponCode = string.Empty; var fakeBasket = GetFakeBasket(fakeBuyerId); var fakeQuantities = new Dictionary() { @@ -75,9 +79,9 @@ namespace UnitTest.Basket.Application .Returns(Task.FromResult(fakeBasket)); //Act - var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); + var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object, _couponServiceMock.Object); orderController.ControllerContext.HttpContext = _contextMock.Object; - var actionResult = await orderController.Index(fakeQuantities, action); + var actionResult = await orderController.Index(fakeQuantities, couponCode, action); //Assert var redirectToActionResult = Assert.IsType(actionResult); @@ -95,7 +99,7 @@ namespace UnitTest.Basket.Application .Returns(Task.FromResult(1)); //Act - var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); + var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object, _couponServiceMock.Object); orderController.ControllerContext.HttpContext = _contextMock.Object; var actionResult = await orderController.AddToCart(fakeCatalogItem); diff --git a/src/Web/WebMVC/Controllers/CartController.cs b/src/Web/WebMVC/Controllers/CartController.cs index d7e577c25..963ca5e6a 100644 --- a/src/Web/WebMVC/Controllers/CartController.cs +++ b/src/Web/WebMVC/Controllers/CartController.cs @@ -14,13 +14,15 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers { private readonly IBasketService _basketSvc; private readonly ICatalogService _catalogSvc; + private readonly ICouponService _couponSvc; private readonly IIdentityParser _appUserParser; - public CartController(IBasketService basketSvc, ICatalogService catalogSvc, IIdentityParser appUserParser) + public CartController(IBasketService basketSvc, ICatalogService catalogSvc, IIdentityParser appUserParser, ICouponService couponService) { _basketSvc = basketSvc; _catalogSvc = catalogSvc; _appUserParser = appUserParser; + _couponSvc = couponService; } public async Task Index() @@ -40,14 +42,24 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers return View(); } - [HttpPost] - public async Task Index(Dictionary quantities, string action) + public async Task Index(Dictionary quantities, string couponCode, string action) { try { var user = _appUserParser.Parse(HttpContext.User); - var basket = await _basketSvc.SetQuantities(user, quantities); + + var vm = await _basketSvc.GetBasket(user); + if (action == "[ Submit Coupon ]") + { + var updatedBasket = await _couponSvc.Apply(vm, couponCode); + await _basketSvc.ApplyCoupon(updatedBasket); + } + if (action == "[ Update ]") + { + var basket = await _basketSvc.SetQuantities(user, quantities); + } + if (action == "[ Checkout ]") { return RedirectToAction("Create", "Order"); @@ -60,7 +72,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers return View(); } - public async Task AddToCart(CatalogItem productDetails) { try diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index ec0afb456..d89350ef8 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -57,22 +57,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { var uri = API.Basket.UpdateBasket(_basketByPassUrl); - var basketUpdate = new - { - BuyerId = basket.BuyerId, - Items = basket.Items.Select(item => new - { - Id = item.Id, - ProductId = item.ProductId, - ProductName = item.ProductName, - UnitPrice = item.UnitPrice * (decimal)0.10, - OldUnitPrice = item.UnitPrice, - Quantity = item.Quantity, - PictureUrl = item.PictureUrl - }).ToArray() - }; - - var basketContent = new StringContent(JsonConvert.SerializeObject(basketUpdate), System.Text.Encoding.UTF8, "application/json"); + var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json"); var response = await _apiClient.PostAsync(uri, basketContent); @@ -137,7 +122,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { CatalogItemId = productId, BasketId = user.Id, - Quantity = 1 + Quantity = 1, + isDiscounted = false }; var basketContent = new StringContent(JsonConvert.SerializeObject(newItem), System.Text.Encoding.UTF8, "application/json"); diff --git a/src/Web/WebMVC/Services/CouponService.cs b/src/Web/WebMVC/Services/CouponService.cs index 3f6c9673c..4b8741c1c 100644 --- a/src/Web/WebMVC/Services/CouponService.cs +++ b/src/Web/WebMVC/Services/CouponService.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; @@ -14,23 +15,69 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public class CouponService : ICouponService { - public Basket Apply(Basket basket) + private readonly IOptions _settings; + private readonly HttpClient _apiClient; + private readonly ILogger _logger; + + public CouponService(HttpClient httpClient, IOptions settings, ILogger logger) + { + _apiClient = httpClient; + _settings = settings; + _logger = logger; + } + public async Task Apply(Basket basket, string couponCode) { - //Basket updatedBasket = new Basket(); - //updatedBasket.BuyerId = basket.BuyerId; - //// Todo: Stub for now, should reach out to a coupon microservice - //if (basket.CouponCode == "SM360") - //{ - // foreach (BasketItem item in basket.Items) - // { - // item.UnitPrice = item.UnitPrice - (item.UnitPrice * (decimal)0.1); - // } - //} - //else - //{ - throw new System.NotImplementedException(); - //} - //return basket; + decimal discount = 0; + + List Items = new List(); + // Todo: Stub for now, should reach out to a coupon microservice + // Coupon for smart hotel 360 provides a 10% discount + if (couponCode == "SH360") + { + foreach (BasketItem Item in basket.Items.Select(item => item).ToList()) + { + if (Item.isDiscounted == false) + { + Items.Add( + new BasketItem + { + Id = Item.Id, + ProductId = Item.ProductId, + ProductName = Item.ProductName, + UnitPrice = Item.UnitPrice * (decimal)(1 - 0.1), + OldUnitPrice = Item.UnitPrice, + Quantity = Item.Quantity, + PictureUrl = Item.PictureUrl, + isDiscounted = true + }); + + } + } + } + else + { + foreach (BasketItem Item in basket.Items.Select(item => item).ToList()) + { + Items.Add( + new BasketItem + { + Id = Item.Id, + ProductId = Item.ProductId, + ProductName = Item.ProductName, + UnitPrice = Item.UnitPrice, + OldUnitPrice = Item.OldUnitPrice, + Quantity = Item.Quantity, + PictureUrl = Item.PictureUrl, + isDiscounted = Item.isDiscounted + }); + } + } + Basket basketUpdate = new Basket + { + BuyerId = basket.BuyerId, + Items = Items + }; + return basketUpdate; } } } diff --git a/src/Web/WebMVC/Services/ICouponService.cs b/src/Web/WebMVC/Services/ICouponService.cs index fefeaa02e..ec4bcb846 100644 --- a/src/Web/WebMVC/Services/ICouponService.cs +++ b/src/Web/WebMVC/Services/ICouponService.cs @@ -8,6 +8,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public interface ICouponService { - Basket Apply(Basket basket); + Task Apply(Basket basket, string couponCode); } } diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 427e234c7..694e2a7a9 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -160,6 +160,9 @@ namespace Microsoft.eShopOnContainers.WebMVC services.AddHttpClient() .AddDevspacesSupport(); + services.AddHttpClient() + .AddDevspacesSupport(); + services.AddHttpClient() .AddHttpMessageHandler() .AddHttpMessageHandler() diff --git a/src/Web/WebMVC/ViewModels/BasketItem.cs b/src/Web/WebMVC/ViewModels/BasketItem.cs index 115871f96..2117cebe9 100644 --- a/src/Web/WebMVC/ViewModels/BasketItem.cs +++ b/src/Web/WebMVC/ViewModels/BasketItem.cs @@ -7,6 +7,7 @@ public string ProductName { get; init; } public decimal UnitPrice { get; init; } public decimal OldUnitPrice { get; init; } + public bool isDiscounted { get; set; } public int Quantity { get; init; } public string PictureUrl { get; init; } } diff --git a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml index 19e1bd9b1..cf37304ee 100644 --- a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml +++ b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml @@ -34,23 +34,27 @@ { var item = Model.Items[i]; -
-
- -
-
@item.ProductName
-
$ @item.UnitPrice.ToString("N2")
-
- - -
-
$ @Math.Round(item.Quantity * item.UnitPrice, 2).ToString("N2")
-
+
+
+ +
+
@item.ProductName
+
$ @item.UnitPrice.ToString("N2")
+
+ + +
+
$ @Math.Round(item.Quantity * item.UnitPrice, 2).ToString("N2")
+
- @if (item.OldUnitPrice != 0) + @if (item.OldUnitPrice != 0 && item.isDiscounted == false) + { + + } + @if (item.OldUnitPrice != 0 && item.isDiscounted == true) { - + }

@@ -68,9 +72,20 @@
-
+
+
+ + +
+
- +
} - + - + diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.html b/src/Web/WebSPA/Client/modules/basket/basket.component.html index 72edb9ace..e45a1bbd3 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.html +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.html @@ -36,7 +36,9 @@

- + + +