diff --git a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs index 384e38e2f..f8ed348af 100644 --- a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs +++ b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs @@ -800,6 +800,9 @@ g: var topupInvoice = invoices.Single(invoice => invoice.ItemCode == "g"); Assert.Equal(0, topupInvoice.Price); Assert.Equal("new", topupInvoice.Status); + var client = await user.CreateClient(); + var inv = await client.GetInvoice(user.StoreId, topupInvoice.Id); + Assert.Equal(InvoiceType.TopUp, inv.Type); } } } diff --git a/BTCPayServer.Tests/POSTests.cs b/BTCPayServer.Tests/POSTests.cs index db39f9803..73f900c5f 100644 --- a/BTCPayServer.Tests/POSTests.cs +++ b/BTCPayServer.Tests/POSTests.cs @@ -12,6 +12,7 @@ using BTCPayServer.Plugins.PointOfSale; using BTCPayServer.Plugins.PointOfSale.Controllers; using BTCPayServer.Plugins.PointOfSale.Models; using BTCPayServer.Services.Apps; +using BTCPayServer.Views.Stores; using Microsoft.AspNetCore.Mvc; using Microsoft.Playwright; using Newtonsoft.Json.Linq; @@ -474,6 +475,66 @@ goodies: } } + [Fact] + [Trait("Playwright", "Playwright")] + public async Task CanUsePOSProductList() + { + await using var s = CreatePlaywrightTester(); + await s.StartAsync(); + + await s.RegisterNewUser(); + s.AsTestAccount(); + await s.GoToHome(); + await s.CreateNewStore(); + await s.GoToStore(); + await s.AddDerivationScheme(); + + // Let's check Custom amount works as expected + await s.CreateApp("PointOfSale"); + var appUrl = s.Page.Url; + await s.Page.FillAsync("#Currency", "BTC"); + await s.Page.SetCheckedAsync("#ShowCustomAmount", true); + await s.ClickPagePrimary(); + + var o = s.Page.Context.WaitForPageAsync(); + await s.Page.ClickAsync("#ViewApp"); + await using (_ = await s.SwitchPage(o)) + { + await s.Page.FillAsync("#card_herbal-tea [name='amount']", "123"); + await s.Page.PressAsync("#card_herbal-tea [name='amount']", "Enter"); + + await AssertInvoiceAmount(s, "123.00000000 BTC"); + await s.Page.GoBackAsync(); + + await s.Page.FillAsync("#card_custom_amount [name='amount']", "124"); + await s.Page.PressAsync("#card_custom_amount [name='amount']", "Enter"); + + await AssertInvoiceAmount(s, "124.00000000 BTC"); + await s.Page.GoBackAsync(); + } + + await s.GoToStore(); + await s.GoToStore(StoreNavPages.Forms); + await s.ClickPagePrimary(); + await s.Page.FillAsync("[name='Name']", "test"); + await s.Page.ClickAsync("[name='newField1']"); + await s.Page.SelectOptionAsync("#field-editor-field-type", "number"); + await s.Page.FillAsync("#field-editor-field-name", "invoice_amount_adjustment"); + await s.Page.PressAsync("#field-editor-field-name", "Enter"); + await s.ClickPagePrimary(); + + await s.GoToUrl(appUrl); + await s.Page.SelectOptionAsync("#FormId", new SelectOptionValue { Label = "test" }); + await s.ClickPagePrimary(); + } + + private static async Task AssertInvoiceAmount(PlaywrightTester s, string expectedAmount) + { + var el = await s.Page.WaitForSelectorAsync("#AmountDue"); + var content = await el!.TextContentAsync(); + Assert.Equal(expectedAmount.NormalizeWhitespaces(), content.NormalizeWhitespaces()); + } + [Fact] [Trait("Playwright", "Playwright")] public async Task CanUsePOSKeypad() diff --git a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs index e9ae4595f..f17df5305 100644 --- a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs +++ b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs @@ -215,15 +215,16 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers { jposData.Cart = new PosAppCartItem[] { new() { Id = choiceKey, Count = 1, Price = amount ?? 0 } }; } + jposData.Cart ??= []; if (currentView is PosViewType.Print) return NotFound(); - if (currentView is PosViewType.Cart or PosViewType.Static && jposData.Cart.Length == 0) + if (currentView is PosViewType.Cart && jposData.Cart.Length == 0) return NotFound(); - if (jposData.Amounts is null && - currentView == PosViewType.Light && + if (string.IsNullOrEmpty(choiceKey) && + jposData.Amounts is null && amount is { } o) { order.AddLine(new("", 1, o, settings.DefaultTaxRate)); @@ -323,7 +324,8 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers var receiptData = new PosReceiptData(); var summary = order.Calculate(); - var isTopup = selectedChoices?.FirstOrDefault()?.Price is null && currentView == PosViewType.Static; + var isTopup = currentView == PosViewType.Static && + selectedChoices.Any(c => c.PriceType == AppItemPriceType.Topup); if (!isTopup) { jposData.ItemsTotal = summary.ItemsTotal; diff --git a/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs b/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs index 6bbce9f6a..da0732a68 100644 --- a/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs +++ b/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs @@ -10,7 +10,7 @@ public class PoSOrder private readonly int _decimals; decimal _discount; decimal _tip; - List ItemLines = new(); + public List ItemLines = new(); public PoSOrder(int decimals) { diff --git a/BTCPayServer/Views/Shared/PointOfSale/Public/Static.cshtml b/BTCPayServer/Views/Shared/PointOfSale/Public/Static.cshtml index 3361e8d83..f95c24305 100644 --- a/BTCPayServer/Views/Shared/PointOfSale/Public/Static.cshtml +++ b/BTCPayServer/Views/Shared/PointOfSale/Public/Static.cshtml @@ -35,7 +35,7 @@ : item.BuyButtonText; buttonText = buttonText.Replace("{0}", formatted).Replace("{Price}", formatted); -
+
@if (!string.IsNullOrWhiteSpace(item.Image)) { @@ -92,7 +92,7 @@ } @if (Model.ShowCustomAmount) { -
+
Custom Amount