From 5ab55e71e0443f90d40ca31bb530bd929d449acf Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 5 Apr 2023 15:42:23 +0200 Subject: [PATCH] Add cart items to receipt (#4851) --- .../Controllers/UIPointOfSaleController.cs | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs index 1709c88eb..b6b8d66b4 100644 --- a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs +++ b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs @@ -20,6 +20,7 @@ using BTCPayServer.Forms.Models; using BTCPayServer.ModelBinders; using BTCPayServer.Models; using BTCPayServer.Plugins.PointOfSale.Models; +using BTCPayServer.Services; using BTCPayServer.Services.Apps; using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Rates; @@ -46,12 +47,14 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers CurrencyNameTable currencies, StoreRepository storeRepository, UIInvoiceController invoiceController, - FormDataService formDataService) + FormDataService formDataService, + DisplayFormatter displayFormatter) { _currencies = currencies; _appService = appService; _storeRepository = storeRepository; _invoiceController = invoiceController; + _displayFormatter = displayFormatter; FormDataService = formDataService; } @@ -59,6 +62,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers private readonly StoreRepository _storeRepository; private readonly AppService _appService; private readonly UIInvoiceController _invoiceController; + private readonly DisplayFormatter _displayFormatter; public FormDataService FormDataService { get; } [HttpGet("/")] @@ -157,9 +161,11 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers decimal? price; Dictionary paymentMethods = null; ViewPointOfSaleViewModel.Item choice = null; + Dictionary cartItems = null; + ViewPointOfSaleViewModel.Item[] choices = null; if (!string.IsNullOrEmpty(choiceKey)) { - var choices = _appService.GetPOSItems(settings.Template, settings.Currency); + choices = _appService.GetPOSItems(settings.Template, settings.Currency); choice = choices.FirstOrDefault(c => c.Id == choiceKey); if (choice == null) return NotFound(); @@ -194,10 +200,11 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers price = amount; title = settings.Title; //if cart IS enabled and we detect posdata that matches the cart system's, check inventory for the items + if (currentView == PosViewType.Cart && - AppService.TryParsePosCartItems(jposData, out var cartItems)) + AppService.TryParsePosCartItems(jposData, out cartItems)) { - var choices = _appService.GetPOSItems(settings.Template, settings.Currency); + choices = _appService.GetPOSItems(settings.Template, settings.Currency); var expectedMinimumAmount = 0m; foreach (var cartItem in cartItems) { @@ -298,6 +305,50 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers { entity.Metadata.OrderUrl = Request.GetDisplayUrl(); entity.Metadata.PosData = jposData; + var receiptData = new JObject(); + if (choice is not null) + { + receiptData = JObject.FromObject(new Dictionary() + { + {"Title", choice.Title}, {"Description", choice.Description}, + }); + } + else if (jposData is not null) + { + var appPosData = jposData.ToObject(); + receiptData = new JObject(); + if (cartItems is not null && choices is not null) + { + var selectedChoices = choices.Where(item => cartItems.Keys.Contains(item.Id)) + .ToDictionary(item => item.Id); + var cartData = new JObject(); + foreach (KeyValuePair cartItem in cartItems) + { + if (selectedChoices.TryGetValue(cartItem.Key, out var selectedChoice)) + { + cartData.Add(selectedChoice.Title ?? selectedChoice.Id, + $"{(selectedChoice.Price.Value is null ? "Any price" : $"{_displayFormatter.Currency((decimal)selectedChoice.Price.Value, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol)}")} x {cartItem.Value} = {(selectedChoice.Price.Value is null ? "Any price" : $"{_displayFormatter.Currency(((decimal)selectedChoice.Price.Value) * cartItem.Value, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol)}")}"); + + } + } + receiptData.Add("Cart", cartData); + } + + if (appPosData.DiscountAmount > 0) + { + receiptData.Add("Discount", + $"{_displayFormatter.Currency(appPosData.DiscountAmount, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol) } {(appPosData.DiscountPercentage > 0 ? $"({appPosData.DiscountPercentage}%)" : string.Empty)}"); + } + + if (appPosData.Tip > 0) + { + receiptData.Add("Tip", + $"{_displayFormatter.Currency(appPosData.Tip, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol) }"); + } + + } + entity.Metadata.SetAdditionalData("receiptData", receiptData); + if (formResponseJObject is null) return; var meta = entity.Metadata.ToJObject(); meta.Merge(formResponseJObject);