mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
POS: Account for custom amount in cart view (#5151)
* Add failing test * Account for custom amount * Test fix
This commit is contained in:
@@ -2161,6 +2161,70 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Contains("1 222,21 €", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
Assert.Contains("1 222,21 €", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Selenium", "Selenium")]
|
||||||
|
[Trait("Lightning", "Lightning")]
|
||||||
|
public async Task CanUsePOSCart()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
Assert.Contains("App successfully created", s.FindAlertMessage().Text);
|
||||||
|
s.Driver.FindElement(By.CssSelector("label[for='DefaultView_Cart']")).Click();
|
||||||
|
s.Driver.FindElement(By.Id("Currency")).SendKeys("EUR");
|
||||||
|
s.Driver.FindElement(By.Id("ShowCustomAmount")).Click();
|
||||||
|
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.Id("js-cart-list"));
|
||||||
|
Assert.Empty(s.Driver.FindElements(By.CssSelector("#js-cart-list tbody tr")));
|
||||||
|
Assert.Equal("0,00 €", s.Driver.FindElement(By.Id("CartTotal")).Text);
|
||||||
|
Assert.False(s.Driver.FindElement(By.Id("CartClear")).Displayed);
|
||||||
|
|
||||||
|
// Select and clear
|
||||||
|
s.Driver.FindElement(By.CssSelector(".card.js-add-cart:nth-child(1)")).Click();
|
||||||
|
Assert.Single(s.Driver.FindElements(By.CssSelector("#js-cart-list tbody tr")));
|
||||||
|
s.Driver.FindElement(By.Id("CartClear")).Click();
|
||||||
|
Assert.Empty(s.Driver.FindElements(By.CssSelector("#js-cart-list tbody tr")));
|
||||||
|
Thread.Sleep(250);
|
||||||
|
|
||||||
|
// Select items
|
||||||
|
s.Driver.FindElement(By.CssSelector(".card.js-add-cart:nth-child(2)")).Click();
|
||||||
|
Thread.Sleep(250);
|
||||||
|
s.Driver.FindElement(By.CssSelector(".card.js-add-cart:nth-child(1)")).Click();
|
||||||
|
Thread.Sleep(250);
|
||||||
|
Assert.Equal(2, s.Driver.FindElements(By.CssSelector("#js-cart-list tbody tr")).Count);
|
||||||
|
Assert.Equal("2,00 €", s.Driver.FindElement(By.Id("CartTotal")).Text);
|
||||||
|
|
||||||
|
// Custom amount
|
||||||
|
s.Driver.FindElement(By.Id("CartCustomAmount")).SendKeys("1.5");
|
||||||
|
s.Driver.FindElement(By.Id("CartTotal")).Click();
|
||||||
|
Assert.Equal("3,50 €", s.Driver.FindElement(By.Id("CartTotal")).Text);
|
||||||
|
s.Driver.FindElement(By.Id("js-cart-confirm")).Click();
|
||||||
|
|
||||||
|
// Pay
|
||||||
|
Assert.Equal("3,50 €", s.Driver.FindElement(By.Id("CartSummaryTotal")).Text);
|
||||||
|
s.Driver.FindElement(By.Id("js-cart-pay")).Click();
|
||||||
|
|
||||||
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
|
s.Driver.FindElement(By.Id("DetailsToggle")).Click();
|
||||||
|
s.Driver.WaitForElement(By.Id("PaymentDetails-TotalFiat"));
|
||||||
|
Assert.Contains("3,50 €", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Selenium", "Selenium")]
|
[Trait("Selenium", "Selenium")]
|
||||||
[Trait("Lightning", "Lightning")]
|
[Trait("Lightning", "Lightning")]
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? amount = null,
|
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? amount = null,
|
||||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? tip = null,
|
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? tip = null,
|
||||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? discount = null,
|
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? discount = null,
|
||||||
|
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? customAmount = null,
|
||||||
string email = null,
|
string email = null,
|
||||||
string orderId = null,
|
string orderId = null,
|
||||||
string notificationUrl = null,
|
string notificationUrl = null,
|
||||||
@@ -232,9 +233,11 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
|
|
||||||
price += expectedCartItemPrice * cartItem.Value;
|
price += expectedCartItemPrice * cartItem.Value;
|
||||||
}
|
}
|
||||||
if (discount is decimal d)
|
if (customAmount is { } c)
|
||||||
|
price += c;
|
||||||
|
if (discount is { } d)
|
||||||
price -= price * d/100.0m;
|
price -= price * d/100.0m;
|
||||||
if (tip is decimal t)
|
if (tip is { } t)
|
||||||
price += t;
|
price += t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,17 +51,15 @@
|
|||||||
<td class="align-middle text-end">{price}</td>
|
<td class="align-middle text-end">{price}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="template-cart-item-image" type="text/template">
|
<script id="template-cart-item-image" type="text/template">
|
||||||
<img class="cart-item-image" src="{image}" alt="">
|
<img class="cart-item-image" src="{image}" alt="">
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="template-cart-custom-amount" type="text/template">
|
<script id="template-cart-custom-amount" type="text/template">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text"><i class="fa fa-shopping-cart fa-fw"></i></span>
|
<span class="input-group-text"><i class="fa fa-shopping-cart fa-fw"></i></span>
|
||||||
<input class="js-cart-custom-amount form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="Pay what you want">
|
<input class="js-cart-custom-amount form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="Pay what you want" id="CartCustomAmount">
|
||||||
<div class="input-group-text">
|
<div class="input-group-text">
|
||||||
<a class="js-cart-custom-amount-remove btn btn-danger" href="#"><i class="fa fa-times"></i></a>
|
<a class="js-cart-custom-amount-remove btn btn-danger" href="#"><i class="fa fa-times"></i></a>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,34 +67,32 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="template-cart-extra" type="text/template">
|
<script id="template-cart-extra" type="text/template">
|
||||||
@if (Model.ShowCustomAmount)
|
@if (Model.ShowCustomAmount)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="5" class="border-0 pb-0">
|
<th colspan="5" class="border-0 pb-0">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text"><i class="fa fa-shopping-cart fa-fw"></i></span>
|
<span class="input-group-text"><i class="fa fa-shopping-cart fa-fw"></i></span>
|
||||||
<input class="js-cart-custom-amount form-control" type="number" min="0" step="@Model.Step" name="amount" value="{customAmount}" placeholder="Pay what you want">
|
<input class="js-cart-custom-amount form-control" type="number" min="0" step="@Model.Step" name="amount" value="{customAmount}" placeholder="Pay what you want" id="CartCustomAmount">
|
||||||
<a class="js-cart-custom-amount-remove btn btn-danger" href="#"><i class="fa fa-times"></i></a>
|
<a class="js-cart-custom-amount-remove btn btn-danger" href="#"><i class="fa fa-times"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@if (Model.ShowDiscount)
|
@if (Model.ShowDiscount)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="5" class="border-top-0">
|
<th colspan="5" class="border-top-0">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text"><i class="fa fa-percent fa-fw"></i></span>
|
<span class="input-group-text"><i class="fa fa-percent fa-fw"></i></span>
|
||||||
<input class="js-cart-discount form-control" type="number" min="0" step="@Model.Step" value="{discount}" name="discount" placeholder="Discount in %">
|
<input class="js-cart-discount form-control" type="number" min="0" step="@Model.Step" value="{discount}" name="discount" placeholder="Discount in %" id="CartDiscount">
|
||||||
<a class="js-cart-discount-remove btn btn-danger" href="#"><i class="fa fa-times"></i></a>
|
<a class="js-cart-discount-remove btn btn-danger" href="#"><i class="fa fa-times"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="template-cart-tip" type="text/template">
|
<script id="template-cart-tip" type="text/template">
|
||||||
@if (Model.EnableTips)
|
@if (Model.EnableTips)
|
||||||
{
|
{
|
||||||
@@ -133,14 +129,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="template-cart-total" type="text/template">
|
<script id="template-cart-total" type="text/template">
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="1" class="pb-4 h4">Total</th>
|
<th colspan="1" class="pb-4 h4">Total</th>
|
||||||
<th colspan="4" class="pb-4 h4 text-end">
|
<th colspan="4" class="pb-4 h4 text-end">
|
||||||
<span class="js-cart-total">{total}</span>
|
<span class="js-cart-total" id="CartTotal">{total}</span>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</script>
|
</script>
|
||||||
@@ -162,7 +157,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="border-0 pb-0 h6">Total products</td>
|
<td class="border-0 pb-0 h6">Total products</td>
|
||||||
<td align="right" class="border-0 pb-0 h6">
|
<td class="text-end border-0 pb-0 h6">
|
||||||
<span class="js-cart-summary-products text-nowrap"></span>
|
<span class="js-cart-summary-products text-nowrap"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -170,8 +165,8 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td class="border-0 pb-y h6">Discount</td>
|
<td class="border-0 pb-y h6">Discount</td>
|
||||||
<td align="right" class="border-0 pb-y h6">
|
<td class="text-end border-0 pb-y h6">
|
||||||
<span class="js-cart-summary-discount text-nowrap"></span>
|
<span class="js-cart-summary-discount text-nowrap" id="CartSummaryDiscount"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@@ -179,15 +174,15 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td class="border-top-0 pt-0 h6">Tip</td>
|
<td class="border-top-0 pt-0 h6">Tip</td>
|
||||||
<td align="right" class="border-top-0 pt-0 h6">
|
<td class="text-end border-top-0 pt-0 h6">
|
||||||
<span class="js-cart-summary-tip text-nowrap"></span>
|
<span class="js-cart-summary-tip text-nowrap" id="CartSummaryTip"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="h3 table-light">Total</td>
|
<td class="h3 table-light">Total</td>
|
||||||
<td class="h3 table-light text-end">
|
<td class="h3 table-light text-end">
|
||||||
<span class="js-cart-summary-total text-nowrap"></span>
|
<span class="js-cart-summary-total text-nowrap" id="CartSummaryTotal"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -202,10 +197,11 @@
|
|||||||
asp-antiforgery="false"
|
asp-antiforgery="false"
|
||||||
data-buy
|
data-buy
|
||||||
>
|
>
|
||||||
<input id="js-cart-amount" class="form-control" type="hidden" name="amount">
|
<input id="js-cart-amount" type="hidden" name="amount">
|
||||||
<input id="js-cart-tip" class="form-control" type="hidden" name="tip">
|
<input id="js-cart-custom-amount" type="hidden" name="customAmount">
|
||||||
<input id="js-cart-discount" class="form-control" type="hidden" name="discount">
|
<input id="js-cart-tip" type="hidden" name="tip">
|
||||||
<input id="js-cart-posdata" class="form-control" type="hidden" name="posdata">
|
<input id="js-cart-discount" type="hidden" name="discount">
|
||||||
|
<input id="js-cart-posdata" type="hidden" name="posdata">
|
||||||
<button id="js-cart-pay" class="btn btn-primary btn-lg" type="submit">
|
<button id="js-cart-pay" class="btn btn-primary btn-lg" type="submit">
|
||||||
<b>@Model.CustomButtonText</b>
|
<b>@Model.CustomButtonText</b>
|
||||||
</button>
|
</button>
|
||||||
@@ -318,7 +314,7 @@
|
|||||||
<a class="js-cart btn btn-sm bg-white text-black pull-right ms-5" href="#">
|
<a class="js-cart btn btn-sm bg-white text-black pull-right ms-5" href="#">
|
||||||
<i class="fa fa-times fa-lg"></i>
|
<i class="fa fa-times fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="js-cart-destroy btn btn-danger pull-right" href="#" style="display: none;">Empty cart <i class="fa fa-trash fa-fw fa-lg"></i></a>
|
<a class="js-cart-destroy btn btn-danger pull-right" href="#" style="display: none;" id="CartClear">Empty cart <i class="fa fa-trash fa-fw fa-lg"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table id="js-cart-list" class="table table-responsive table-light mt-0 mb-0">
|
<table id="js-cart-list" class="table table-responsive table-light mt-0 mb-0">
|
||||||
|
|||||||
@@ -330,9 +330,9 @@ Cart.prototype.updateAmount = function() {
|
|||||||
$('#js-cart-amount').val(this.getTotal(true));
|
$('#js-cart-amount').val(this.getTotal(true));
|
||||||
$('#js-cart-tip').val(this.tip);
|
$('#js-cart-tip').val(this.tip);
|
||||||
$('#js-cart-discount').val(this.discount);
|
$('#js-cart-discount').val(this.discount);
|
||||||
|
$('#js-cart-custom-amount').val(this.customAmount);
|
||||||
}
|
}
|
||||||
Cart.prototype.updatePosData = function() {
|
Cart.prototype.updatePosData = function() {
|
||||||
|
|
||||||
var result = {
|
var result = {
|
||||||
cart: this.content,
|
cart: this.content,
|
||||||
customAmount: this.fromCents(this.getCustomAmount()),
|
customAmount: this.fromCents(this.getCustomAmount()),
|
||||||
|
|||||||
Reference in New Issue
Block a user