From 5f7a68683378eeb4a3eb975b5ccf96d5ff2b3098 Mon Sep 17 00:00:00 2001 From: Nicolas Dorier Date: Tue, 20 May 2025 13:31:29 +0900 Subject: [PATCH] Show tax rate in receipt, adjust keypad display (#6739) * Show tax rate in receipt, adjust keypad display * Adjust small nits in the view --- BTCPayServer.Tests/POSTests.cs | 18 ++++++++-------- .../Controllers/UIPointOfSaleController.cs | 2 ++ BTCPayServer/Plugins/PointOfSale/PoSOrder.cs | 15 +++++++++++++ .../Shared/PointOfSale/Public/Cart.cshtml | 14 +++++++------ .../Shared/PointOfSale/Public/VueLight.cshtml | 2 +- BTCPayServer/Views/Shared/PosData.cshtml | 4 ++-- .../UIInvoice/InvoiceReceiptPrint.cshtml | 3 --- BTCPayServer/wwwroot/pos/common.js | 21 +++++++++++++++++++ BTCPayServer/wwwroot/pos/keypad.js | 12 +++++++++-- 9 files changed, 68 insertions(+), 23 deletions(-) diff --git a/BTCPayServer.Tests/POSTests.cs b/BTCPayServer.Tests/POSTests.cs index 1537e89c4..b229525bc 100644 --- a/BTCPayServer.Tests/POSTests.cs +++ b/BTCPayServer.Tests/POSTests.cs @@ -522,23 +522,23 @@ donation: Assert.Equal("1.234,00", await s.Page.TextContentAsync("#Amount")); Assert.Equal("", await s.Page.TextContentAsync("#Calculation")); await EnterKeypad(s, "+56"); - Assert.Equal("1.234,56", await s.Page.TextContentAsync("#Amount")); + Assert.Equal("0,56", await s.Page.TextContentAsync("#Amount")); Assert.True(await s.Page.IsEnabledAsync("#ModeTablist-discount")); Assert.True(await s.Page.IsEnabledAsync("#ModeTablist-tip")); - await AssertKeypadCalculation(s, "1.234,00 € + 0,56 €"); + await AssertKeypadCalculation(s, "1.234,00 € + 0,56 € = 1.234,56 €"); // Discount: 10% await s.Page.ClickAsync("label[for='ModeTablist-discount']"); await EnterKeypad(s, "10"); - Assert.Contains("1.111,10", await s.Page.TextContentAsync("#Amount")); + Assert.Contains("0,56", await s.Page.TextContentAsync("#Amount")); Assert.Contains("10% discount", await s.Page.TextContentAsync("#Discount")); - await AssertKeypadCalculation(s, "1.234,00 € + 0,56 € - 123,46 € (10%)"); + await AssertKeypadCalculation(s, "1.234,00 € + 0,56 € - 123,46 € (10%) = 1.111,10 €"); // Tip: 10% await s.Page.ClickAsync("label[for='ModeTablist-tip']"); await s.Page.ClickAsync("#Tip-10"); - Assert.Contains("1.222,21", await s.Page.TextContentAsync("#Amount")); - await AssertKeypadCalculation(s, "1.234,00 € + 0,56 € - 123,46 € (10%) + 111,11 € (10%)"); + Assert.Contains("0,56", await s.Page.TextContentAsync("#Amount")); + await AssertKeypadCalculation(s, "1.234,00 € + 0,56 € - 123,46 € (10%) + 111,11 € (10%) = 1.222,21 €"); // Pay await s.Page.ClickAsync("#pay-button"); @@ -580,8 +580,8 @@ donation: await s.Page.ClickAsync("#ItemsListOffcanvas button[data-bs-dismiss='offcanvas']"); await EnterKeypad(s, "123"); - Assert.Contains("4,65", await s.Page.TextContentAsync("#Amount")); - await AssertKeypadCalculation(s, "2 x Green Tea (1,00 €) = 2,00 € + 1 x Black Tea (1,00 €) = 1,00 € + 1,23 € + 0,42 € (10%)"); + Assert.Contains("1,23", await s.Page.TextContentAsync("#Amount")); + await AssertKeypadCalculation(s, "2 x Green Tea (1,00 €) = 2,00 € + 1 x Black Tea (1,00 €) = 1,00 € + 1,23 € + 0,42 € (10%) = 4,65 €"); // Pay await s.Page.ClickAsync("#pay-button"); @@ -604,7 +604,7 @@ donation: ], Sums = [ new("Subtotal", "4,23 €"), - new("Tax", "0,42 €"), + new("Tax", "0,42 € (10%)"), new("Total", "4,65 €") ] }); diff --git a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs index 2e0cc6c38..729447c7a 100644 --- a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs +++ b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs @@ -345,6 +345,8 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers if (jposData.Tax > 0) { var taxFormatted = _displayFormatter.Currency(jposData.Tax, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol); + if (order.GetTaxRate() is { } r) + taxFormatted = $"{taxFormatted} ({r:0.######}%)"; receiptData.Tax = taxFormatted; } diff --git a/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs b/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs index a74445075..6bbce9f6a 100644 --- a/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs +++ b/BTCPayServer/Plugins/PointOfSale/PoSOrder.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Linq; namespace BTCPayServer.Plugins.PointOfSale; @@ -64,6 +65,20 @@ public class PoSOrder _tip = Round(tip); } + /// + /// Returns the tax rate of the items in the cart. + /// If the tax rates are not all the same, returns null. + /// If the cart is empty, returns null. + /// Else, returns the tax rate shared by all items + /// + /// + public decimal? GetTaxRate() + { + if (!ItemLines.Any()) + return null; + return ItemLines.GroupBy(i => i.TaxRate).Count() == 1 ? ItemLines[0].TaxRate : null; + } + /// /// /// diff --git a/BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml b/BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml index ca941cf22..357c3be96 100644 --- a/BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml +++ b/BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml @@ -191,9 +191,11 @@ Discount -
- - % +
+
+ + % +
@@ -233,7 +235,7 @@ Discount - {{ formatCurrency(discountNumeric, true) }} ({{discountPercent}}%) + {{ formatCurrency(discountNumeric, true) }} ({{discountPercent}}%) @@ -243,13 +245,13 @@ Tip - {{ formatCurrency(tipNumeric, true) }} ({{tipPercent}}%) + {{ formatCurrency(tipNumeric, true) }} ({{tipPercent}}%) Taxes - {{ formatCurrency(taxNumeric, true) }} + {{ formatCurrency(taxNumeric, true) }} ({{taxPercent}}%) diff --git a/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml b/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml index 745b772ad..af9d470f2 100644 --- a/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml +++ b/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml @@ -22,7 +22,7 @@
{{currencyCode}}
-
{{ formatCurrency(totalNumeric, false) }}
+
{{ formatCurrency(lastAmount, false) }}
{{ calculation }}
diff --git a/BTCPayServer/Views/Shared/PosData.cshtml b/BTCPayServer/Views/Shared/PosData.cshtml index 49c99afde..c820de536 100644 --- a/BTCPayServer/Views/Shared/PosData.cshtml +++ b/BTCPayServer/Views/Shared/PosData.cshtml @@ -34,7 +34,7 @@ @if (posData.ItemsTotal != null) { - + Items total @posData.ItemsTotal @@ -48,7 +48,7 @@ } @if (posData.Subtotal != null) { - + Subtotal @posData.Subtotal diff --git a/BTCPayServer/Views/UIInvoice/InvoiceReceiptPrint.cshtml b/BTCPayServer/Views/UIInvoice/InvoiceReceiptPrint.cshtml index 02029e736..9b6c031d2 100644 --- a/BTCPayServer/Views/UIInvoice/InvoiceReceiptPrint.cshtml +++ b/BTCPayServer/Views/UIInvoice/InvoiceReceiptPrint.cshtml @@ -139,9 +139,6 @@ @if (posData.ItemsTotal != null) { - -
- Items total @posData.ItemsTotal diff --git a/BTCPayServer/wwwroot/pos/common.js b/BTCPayServer/wwwroot/pos/common.js index 09d6201ad..21f9959c0 100644 --- a/BTCPayServer/wwwroot/pos/common.js +++ b/BTCPayServer/wwwroot/pos/common.js @@ -50,6 +50,21 @@ class PoSOrder { } } + // Returns the tax rate of the items in the cart. + // If the tax rates are not all the same, returns null. + // If the cart is empty, returns null. + // Else, returns the tax rate shared by all items + getTaxRate() { + if (this.itemLines.length === 0) return null; + var rate = this.itemLines[0].taxRate ?? 0; + for (const line of this.itemLines.slice(1)) { + if (rate !== line.taxRate) + { + return null; + } + } + return rate; + } calculate() { const ctx = { discount: 0, @@ -159,6 +174,9 @@ const posCommon = { taxNumeric() { return this.summary.tax; }, + taxPercent() { + return this.posOrder.getTaxRate(); + }, subtotalNumeric () { // We don't want to show the subtotal if there is no tax or tips if (this.summary.priceTaxExcluded === this.summary.priceTaxIncludedWithTips) return 0; @@ -185,6 +203,9 @@ const posCommon = { tipNumeric () { return this.summary.tip; }, + lastAmount() { + return this.amounts[this.amounts.length - 1] = this.amounts[this.amounts.length - 1] || 0; + }, totalNumeric () { return this.summary.priceTaxIncludedWithTips; }, diff --git a/BTCPayServer/wwwroot/pos/keypad.js b/BTCPayServer/wwwroot/pos/keypad.js index a748758cf..2c7ba6171 100644 --- a/BTCPayServer/wwwroot/pos/keypad.js +++ b/BTCPayServer/wwwroot/pos/keypad.js @@ -1,4 +1,5 @@ document.addEventListener("DOMContentLoaded",function () { + const displayFontSize = 64; new Vue({ el: '#app', @@ -39,8 +40,15 @@ document.addEventListener("DOMContentLoaded",function () { if (this.discountNumeric > 0 || this.discountPercentNumeric > 0) calc += ` - ${this.formatCurrency(this.discountNumeric, true)} (${this.discountPercent}%)` if (this.summary.tip > 0) calc += ` + ${this.formatCurrency(this.summary.tip, true)}` if (this.tipPercent) calc += ` (${this.tipPercent}%)` - if (this.summary.tax) calc += ` + ${this.formatCurrency(this.summary.tax, true)}` - if (this.defaultTaxRate) calc += ` (${this.defaultTaxRate}%)` + if (this.summary.tax) + { + calc += ` + ${this.formatCurrency(this.summary.tax, true)}` + if (this.posOrder.getTaxRate()) + { + calc += ` (${this.posOrder.getTaxRate()}%)` + } + } + calc += ` = ${this.formatCurrency(this.summary.priceTaxIncludedWithTips, true)}` return calc } },