mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2026-02-07 23:34:28 +01:00
Show tax rate in receipt, adjust keypad display (#6739)
* Show tax rate in receipt, adjust keypad display * Adjust small nits in the view
This commit is contained in:
@@ -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 €")
|
||||
]
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public decimal? GetTaxRate()
|
||||
{
|
||||
if (!ItemLines.Any())
|
||||
return null;
|
||||
return ItemLines.GroupBy(i => i.TaxRate).Count() == 1 ? ItemLines[0].TaxRate : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
||||
@@ -191,9 +191,11 @@
|
||||
<tr v-if="showDiscount">
|
||||
<th class="align-middle" text-translate="true">Discount</th>
|
||||
<th class="align-middle" colspan="3">
|
||||
<div class="input-group input-group-sm w-100px pull-right">
|
||||
<input class="form-control hide-number-spin" type="number" inputmode="decimal" min="0" step="1" max="100" id="Discount" v-model.number="discountPercent">
|
||||
<span class="input-group-text">%</span>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="input-group input-group-sm w-100px">
|
||||
<input class="form-control hide-number-spin" type="number" inputmode="decimal" min="0" step="1" max="100" id="Discount" v-model.number="discountPercent">
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
@@ -233,7 +235,7 @@
|
||||
<tr v-if="discountNumeric">
|
||||
<td class="align-middle" text-translate="true">Discount</td>
|
||||
<td class="align-middle text-end" id="CartDiscount">
|
||||
<span>{{ formatCurrency(discountNumeric, true) }}</span> <span v-if="discountPercent">({{discountPercent}}%)</span>
|
||||
<span>{{ formatCurrency(discountNumeric, true) }}</span> <span v-if="discountPercent">({{discountPercent}}%)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="subtotalNumeric">
|
||||
@@ -243,13 +245,13 @@
|
||||
<tr v-if="tipNumeric">
|
||||
<td class="align-middle" text-translate="true">Tip</td>
|
||||
<td class="align-middle text-end" id="CartTip">
|
||||
<span>{{ formatCurrency(tipNumeric, true) }}</span> <span v-if="tipPercent">({{tipPercent}}%)</span>
|
||||
<span>{{ formatCurrency(tipNumeric, true) }}</span> <span v-if="tipPercent">({{tipPercent}}%)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="taxNumeric">
|
||||
<td class="align-middle" text-translate="true">Taxes</td>
|
||||
<td class="align-middle text-end" id="CartTax">
|
||||
{{ formatCurrency(taxNumeric, true) }}
|
||||
<span>{{ formatCurrency(taxNumeric, true) }}</span> <span v-if="taxPercent">({{taxPercent}}%)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<input type="hidden" name="posdata" :value="posdata">
|
||||
<div ref="display" class="d-flex flex-column align-items-center px-4 mb-auto">
|
||||
<div class="fw-semibold text-muted" id="Currency">{{currencyCode}}</div>
|
||||
<div class="fw-bold lh-sm" ref="amount" v-bind:style="{ fontSize: `${fontSize}px` }" id="Amount">{{ formatCurrency(totalNumeric, false) }}</div>
|
||||
<div class="fw-bold lh-sm" ref="amount" v-bind:style="{ fontSize: `${fontSize}px` }" id="Amount">{{ formatCurrency(lastAmount, false) }}</div>
|
||||
<div class="text-muted text-center mt-2" id="Calculation">{{ calculation }}</div>
|
||||
</div>
|
||||
<div id="ModeTabs" class="tab-content mb-n2" v-if="showDiscount || enableTips">
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<tfoot style="border-top-width:0">
|
||||
@if (posData.ItemsTotal != null)
|
||||
{
|
||||
<tr style="border-top-width:3px">
|
||||
<tr>
|
||||
<th text-translate="true">Items total</th>
|
||||
<td class="text-end">@posData.ItemsTotal</td>
|
||||
</tr>
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
@if (posData.Subtotal != null)
|
||||
{
|
||||
<tr style="border-top-width:3px">
|
||||
<tr>
|
||||
<th text-translate="true">Subtotal</th>
|
||||
<td class="text-end">@posData.Subtotal</td>
|
||||
</tr>
|
||||
|
||||
@@ -139,9 +139,6 @@
|
||||
|
||||
@if (posData.ItemsTotal != null)
|
||||
{
|
||||
<tr>
|
||||
<td colspan="2"><hr class="w-100 my-0"/></td>
|
||||
</tr>
|
||||
<tr class="sums-data">
|
||||
<td class="key text-secondary">Items total</td>
|
||||
<td class="val text-end">@posData.ItemsTotal</td>
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user