From 13203c3e2b3a1d378a969bf70651a95f3fd7f191 Mon Sep 17 00:00:00 2001 From: d11n Date: Thu, 22 Jun 2023 08:57:29 +0200 Subject: [PATCH] Receipt improvements (#5077) * Remove Order ID link * Add separate print version for receipt * Fix POS number handling and add keypad test Fixes #5056. * Add formatting function * Remove OrderUrl for POS, bring back order link for receipt * Update BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs --- BTCPayServer.Tests/SeleniumTests.cs | 75 +++++++++++++++++++ .../Controllers/UIInvoiceController.UI.cs | 4 +- .../Controllers/UIPointOfSaleController.cs | 4 +- BTCPayServer/Services/Invoices/PosAppData.cs | 2 - .../Shared/PointOfSale/Public/VueLight.cshtml | 8 +- BTCPayServer/Views/Shared/PosData.cshtml | 1 - .../Views/UIInvoice/InvoiceReceipt.cshtml | 4 +- .../UIInvoice/InvoiceReceiptPrint.cshtml | 72 ++++++++++++++++++ BTCPayServer/wwwroot/light-pos/app.js | 18 +++-- 9 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 BTCPayServer/Views/UIInvoice/InvoiceReceiptPrint.cshtml diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index ed8f05736..a413585bf 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -2074,6 +2074,81 @@ namespace BTCPayServer.Tests } } + [Fact] + [Trait("Selenium", "Selenium")] + [Trait("Lightning", "Lightning")] + public async Task CanUsePOSKeypad() + { + using var s = CreateSeleniumTester(); + s.Server.ActivateLightning(); + await s.StartAsync(); + + await s.Server.EnsureChannelsSetup(); + + s.RegisterNewUser(true); + s.CreateNewStore(); + s.GoToStore(); + s.AddLightningNode(LightningConnectionType.CLightning, false); + s.Driver.FindElement(By.Id("StoreNav-CreatePointOfSale")).Click(); + s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString()); + s.Driver.FindElement(By.Id("Create")).Click(); + TestUtils.Eventually(() => Assert.Contains("App successfully created", s.FindAlertMessage().Text)); + s.Driver.FindElement(By.CssSelector("label[for='DefaultView_Light']")).Click(); + s.Driver.FindElement(By.Id("Currency")).SendKeys("EUR"); + s.Driver.FindElement(By.Id("CustomTipPercentages")).Clear(); + s.Driver.FindElement(By.Id("CustomTipPercentages")).SendKeys("10,21"); + s.Driver.FindElement(By.Id("SaveSettings")).Click(); + Assert.Contains("App updated", s.FindAlertMessage().Text); + s.Driver.FindElement(By.Id("ViewApp")).Click(); + var windows = s.Driver.WindowHandles; + Assert.Equal(2, windows.Count); + s.Driver.SwitchTo().Window(windows[1]); + s.Driver.WaitForElement(By.ClassName("keypad")); + + // basic checks + Assert.Contains("EUR", s.Driver.FindElement(By.Id("Currency")).Text); + Assert.Contains("0,00", s.Driver.FindElement(By.Id("Amount")).Text); + Assert.Equal("", s.Driver.FindElement(By.Id("Calculation")).Text); + Assert.True(s.Driver.FindElement(By.Id("ModeTablist-amount")).Selected); + Assert.False(s.Driver.FindElement(By.Id("ModeTablist-discount")).Enabled); + Assert.False(s.Driver.FindElement(By.Id("ModeTablist-tip")).Enabled); + + // Amount: 1234,56 + s.Driver.FindElement(By.CssSelector(".keypad [data-key='1']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='2']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='3']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='4']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='.']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='5']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='6']")).Click(); + Assert.Equal("1.234,56", s.Driver.FindElement(By.Id("Amount")).Text); + Assert.True(s.Driver.FindElement(By.Id("ModeTablist-discount")).Enabled); + Assert.True(s.Driver.FindElement(By.Id("ModeTablist-tip")).Enabled); + Assert.Equal("", s.Driver.FindElement(By.Id("Calculation")).Text); + + // Discount: 10% + s.Driver.FindElement(By.CssSelector("label[for='ModeTablist-discount']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='1']")).Click(); + s.Driver.FindElement(By.CssSelector(".keypad [data-key='0']")).Click(); + Assert.Contains("1.111,10", s.Driver.FindElement(By.Id("Amount")).Text); + Assert.Contains("10% discount", s.Driver.FindElement(By.Id("Discount")).Text); + Assert.Contains("1.234,56 € - 123,46 € (10%)", s.Driver.FindElement(By.Id("Calculation")).Text); + + // Tip: 10% + s.Driver.FindElement(By.CssSelector("label[for='ModeTablist-tip']")).Click(); + s.Driver.WaitForElement(By.Id("Tip-Custom")); + s.Driver.FindElement(By.Id("Tip-10")).Click(); + Assert.Contains("1.222,21", s.Driver.FindElement(By.Id("Amount")).Text); + Assert.Contains("1.234,56 € - 123,46 € (10%) + 111,11 € (10%)", s.Driver.FindElement(By.Id("Calculation")).Text); + + // Pay + s.Driver.FindElement(By.Id("pay-button")).Click(); + s.Driver.WaitUntilAvailable(By.Id("Checkout-v2")); + s.Driver.FindElement(By.Id("DetailsToggle")).Click(); + s.Driver.WaitForElement(By.Id("PaymentDetails-TotalFiat")); + Assert.Contains("1 222,21 €", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); + } + [Fact] [Trait("Selenium", "Selenium")] [Trait("Lightning", "Lightning")] diff --git a/BTCPayServer/Controllers/UIInvoiceController.UI.cs b/BTCPayServer/Controllers/UIInvoiceController.UI.cs index 9dc532e77..becf5f85b 100644 --- a/BTCPayServer/Controllers/UIInvoiceController.UI.cs +++ b/BTCPayServer/Controllers/UIInvoiceController.UI.cs @@ -172,7 +172,7 @@ namespace BTCPayServer.Controllers } [HttpGet("i/{invoiceId}/receipt")] - public async Task InvoiceReceipt(string invoiceId) + public async Task InvoiceReceipt(string invoiceId, [FromQuery] bool print = false) { var i = await _InvoiceRepository.GetInvoice(invoiceId); if (i is null) @@ -255,7 +255,7 @@ namespace BTCPayServer.Controllers vm.Payments = receipt.ShowPayments is false ? null : payments; vm.AdditionalData = PosDataParser.ParsePosData(receiptData); - return View(vm); + return View(print ? "InvoiceReceiptPrint" : "InvoiceReceipt", vm); } private string? GetTransactionLink(PaymentMethodId paymentMethodId, string txId) diff --git a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs index 75a7d1a32..edda12647 100644 --- a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs +++ b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs @@ -343,10 +343,8 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers if (appPosData.Tip > 0) { - receiptData.Add("Tip", - $"{_displayFormatter.Currency(appPosData.Tip, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol)}"); + receiptData.Add("Tip", _displayFormatter.Currency(appPosData.Tip, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol)); } - } entity.Metadata.SetAdditionalData("receiptData", receiptData); diff --git a/BTCPayServer/Services/Invoices/PosAppData.cs b/BTCPayServer/Services/Invoices/PosAppData.cs index 531f7da93..2aecb1f13 100644 --- a/BTCPayServer/Services/Invoices/PosAppData.cs +++ b/BTCPayServer/Services/Invoices/PosAppData.cs @@ -1,4 +1,3 @@ -using BTCPayServer.Models.AppViewModels; using BTCPayServer.Plugins.PointOfSale.Models; using Newtonsoft.Json; @@ -54,7 +53,6 @@ public class PosAppCartItemPrice [JsonProperty(PropertyName = "formatted")] public string Formatted { get; set; } - [JsonProperty(PropertyName = "type")] public ViewPointOfSaleViewModel.ItemPriceType Type { get; set; } } diff --git a/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml b/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml index 90e48fc1c..0fd3d8435 100644 --- a/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml +++ b/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml @@ -4,13 +4,13 @@
-
{{srvModel.currencyCode}}
-
{{ formatCurrency(total, false) }}
+
{{srvModel.currencyCode}}
+
{{ formatCurrency(total, false) }}
{{ calculation }}
-
+
{{discountPercent || 0}}% discount
@@ -18,6 +18,7 @@