mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Enhance PosData Viewer & add cart to posdata in POS app (#559)
This commit is contained in:
parent
7ea665d884
commit
30bdfeee37
8 changed files with 96 additions and 50 deletions
|
@ -1629,18 +1629,16 @@ donation:
|
|||
public void PosDataParser_ParsesCorrectly()
|
||||
{
|
||||
var testCases =
|
||||
new List<(string input, Dictionary<string, string> expectedOutput)>()
|
||||
new List<(string input, Dictionary<string, object> expectedOutput)>()
|
||||
{
|
||||
{ (null, new Dictionary<string, string>())},
|
||||
{("", new Dictionary<string, string>())},
|
||||
{("{}", new Dictionary<string, string>())},
|
||||
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})},
|
||||
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})},
|
||||
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})},
|
||||
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})},
|
||||
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}",
|
||||
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
|
||||
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
|
||||
{ (null, new Dictionary<string, object>())},
|
||||
{("", new Dictionary<string, object>())},
|
||||
{("{}", new Dictionary<string, object>())},
|
||||
{("non-json-content", new Dictionary<string, object>(){ {string.Empty, "non-json-content"}})},
|
||||
{("[1,2,3]", new Dictionary<string, object>(){ {string.Empty, "[1,2,3]"}})},
|
||||
{("{ \"key\": \"value\"}", new Dictionary<string, object>(){ {"key", "value"}})},
|
||||
{("{ \"key\": true}", new Dictionary<string, object>(){ {"key", "True"}})},
|
||||
{("{ invalidjson file here}", new Dictionary<string, object>(){ {String.Empty, "{ invalidjson file here}"}})}
|
||||
};
|
||||
|
||||
testCases.ForEach(tuple =>
|
||||
|
@ -1663,18 +1661,16 @@ donation:
|
|||
var controller = tester.PayTester.GetController<InvoiceController>(null);
|
||||
|
||||
var testCases =
|
||||
new List<(string input, Dictionary<string, string> expectedOutput)>()
|
||||
new List<(string input, Dictionary<string, object> expectedOutput)>()
|
||||
{
|
||||
{ (null, new Dictionary<string, string>())},
|
||||
{("", new Dictionary<string, string>())},
|
||||
{("{}", new Dictionary<string, string>())},
|
||||
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})},
|
||||
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})},
|
||||
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})},
|
||||
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})},
|
||||
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}",
|
||||
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
|
||||
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
|
||||
{ (null, new Dictionary<string, object>())},
|
||||
{("", new Dictionary<string, object>())},
|
||||
{("{}", new Dictionary<string, object>())},
|
||||
{("non-json-content", new Dictionary<string, object>(){ {string.Empty, "non-json-content"}})},
|
||||
{("[1,2,3]", new Dictionary<string, object>(){ {string.Empty, "[1,2,3]"}})},
|
||||
{("{ \"key\": \"value\"}", new Dictionary<string, object>(){ {"key", "value"}})},
|
||||
{("{ \"key\": true}", new Dictionary<string, object>(){ {"key", "True"}})},
|
||||
{("{ invalidjson file here}", new Dictionary<string, object>(){ {String.Empty, "{ invalidjson file here}"}})}
|
||||
};
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
|
|
@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Identity;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using static BTCPayServer.Controllers.AppsController;
|
||||
|
||||
|
@ -155,10 +156,11 @@ namespace BTCPayServer.Controllers
|
|||
var store = await _AppsHelper.GetStore(app);
|
||||
var title = settings.Title;
|
||||
var price = request.Amount;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(request.ChoiceKey))
|
||||
{
|
||||
var choices = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency);
|
||||
var choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
|
||||
choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
|
||||
if (choice == null)
|
||||
return NotFound("Incorrect option provided");
|
||||
title = choice.Title;
|
||||
|
@ -211,7 +213,8 @@ namespace BTCPayServer.Controllers
|
|||
string orderId,
|
||||
string notificationUrl,
|
||||
string redirectUrl,
|
||||
string choiceKey)
|
||||
string choiceKey,
|
||||
string posData = null)
|
||||
{
|
||||
var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale);
|
||||
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
||||
|
@ -227,10 +230,11 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
string title = null;
|
||||
var price = 0.0m;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
{
|
||||
var choices = _AppsHelper.Parse(settings.Template, settings.Currency);
|
||||
var choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||
choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
|
@ -249,7 +253,7 @@ namespace BTCPayServer.Controllers
|
|||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||
var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice()
|
||||
{
|
||||
ItemCode = choiceKey ?? string.Empty,
|
||||
ItemCode = choice?.Id,
|
||||
ItemDesc = title,
|
||||
Currency = settings.Currency,
|
||||
Price = price,
|
||||
|
@ -258,6 +262,7 @@ namespace BTCPayServer.Controllers
|
|||
NotificationURL = notificationUrl,
|
||||
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
|
||||
FullNotifications = true,
|
||||
PosData = string.IsNullOrEmpty(posData) ? null : posData
|
||||
}, store, HttpContext.Request.GetAbsoluteRoot());
|
||||
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ using BTCPayServer.Services.Invoices.Export;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using NBXplorer;
|
||||
|
@ -679,9 +680,9 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
public class PosDataParser
|
||||
{
|
||||
public static Dictionary<string, string> ParsePosData(string posData)
|
||||
public static Dictionary<string, object> ParsePosData(string posData)
|
||||
{
|
||||
var result = new Dictionary<string,string>();
|
||||
var result = new Dictionary<string,object>();
|
||||
if (string.IsNullOrEmpty(posData))
|
||||
{
|
||||
return result;
|
||||
|
@ -689,7 +690,6 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
try
|
||||
{
|
||||
|
||||
var jObject =JObject.Parse(posData);
|
||||
foreach (var item in jObject)
|
||||
{
|
||||
|
@ -697,7 +697,14 @@ namespace BTCPayServer.Controllers
|
|||
switch (item.Value.Type)
|
||||
{
|
||||
case JTokenType.Array:
|
||||
result.Add(item.Key, string.Join(',', item.Value.AsEnumerable()));
|
||||
var items = item.Value.AsEnumerable().ToList();
|
||||
for (var i = 0; i < items.Count(); i++)
|
||||
{
|
||||
result.Add($"{item.Key}[{i}]", ParsePosData(items[i].ToString()));
|
||||
}
|
||||
break;
|
||||
case JTokenType.Object:
|
||||
result.Add(item.Key, ParsePosData(item.Value.ToString()));
|
||||
break;
|
||||
default:
|
||||
result.Add(item.Key, item.Value.ToString());
|
||||
|
|
|
@ -143,6 +143,6 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
public DateTimeOffset MonitoringDate { get; internal set; }
|
||||
public List<Data.InvoiceEventData> Events { get; internal set; }
|
||||
public string NotificationEmail { get; internal set; }
|
||||
public Dictionary<string, string> PosData { get; set; }
|
||||
public Dictionary<string, object> PosData { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,6 +194,7 @@
|
|||
<div class="modal-footer bg-light">
|
||||
<form method="post" asp-antiforgery="false" data-buy>
|
||||
<input id="js-cart-amount" class="form-control" type="hidden" name="amount">
|
||||
<input id="js-cart-posdata" class="form-control" type="hidden" name="posdata">
|
||||
<button id="js-cart-pay" class="btn btn-primary btn-lg" type="submit"><b>@Model.CustomButtonText</b></button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -192,24 +192,7 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<h3>Point of Sale Data</h3>
|
||||
<table class="table table-sm table-responsive-md">
|
||||
@foreach (var posDataItem in Model.PosData)
|
||||
{
|
||||
<tr>
|
||||
@if (!string.IsNullOrEmpty(posDataItem.Key))
|
||||
{
|
||||
|
||||
<th>@posDataItem.Key</th>
|
||||
<td>@posDataItem.Value</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
<td colspan="2">@posDataItem.Value</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
<partial name="PosData" model="@Model.PosData"></partial>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
37
BTCPayServer/Views/Invoice/PosData.cshtml
Normal file
37
BTCPayServer/Views/Invoice/PosData.cshtml
Normal file
|
@ -0,0 +1,37 @@
|
|||
@model Dictionary<string, object>
|
||||
|
||||
<table class="table table-sm table-responsive-md">
|
||||
@foreach (var posDataItem in Model)
|
||||
{
|
||||
<tr>
|
||||
@if (!string.IsNullOrEmpty(posDataItem.Key))
|
||||
{
|
||||
<th>@posDataItem.Key</th>
|
||||
<td>
|
||||
@if (posDataItem.Value is string)
|
||||
{
|
||||
@posDataItem.Value
|
||||
}
|
||||
else
|
||||
{
|
||||
<partial name="PosData" model="@posDataItem.Value"/>
|
||||
}
|
||||
|
||||
</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td colspan="2">
|
||||
@if (posDataItem.Value is string)
|
||||
{
|
||||
@posDataItem.Value
|
||||
}
|
||||
else
|
||||
{
|
||||
<partial name="PosData" model="@posDataItem.Value"/>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</table>
|
|
@ -21,6 +21,7 @@ function Cart() {
|
|||
|
||||
this.updateItemsCount();
|
||||
this.updateAmount();
|
||||
this.updatePosData();
|
||||
}
|
||||
|
||||
Cart.prototype.setCustomAmount = function(amount) {
|
||||
|
@ -243,6 +244,7 @@ Cart.prototype.updateAll = function() {
|
|||
this.updateSummaryTotal();
|
||||
this.updateTotal();
|
||||
this.updateAmount();
|
||||
this.updatePosData();
|
||||
}
|
||||
|
||||
// Update number of cart items
|
||||
|
@ -290,6 +292,20 @@ Cart.prototype.updateTip = function(amount) {
|
|||
Cart.prototype.updateAmount = function() {
|
||||
$('#js-cart-amount').val(this.getTotal(true));
|
||||
}
|
||||
Cart.prototype.updatePosData = function() {
|
||||
|
||||
var result = {
|
||||
cart: this.content,
|
||||
customAmount: this.fromCents(this.getCustomAmount()),
|
||||
discountPercentage: this.discount? parseFloat(this.discount): 0,
|
||||
subTotal: this.fromCents(this.getTotalProducts()),
|
||||
discountAmount: this.fromCents(this.getDiscountAmount(this.totalAmount)),
|
||||
tip: this.tip? this.tip: 0,
|
||||
total: this.getTotal(true)
|
||||
};
|
||||
console.warn(result);
|
||||
$('#js-cart-posdata').val(JSON.stringify(result));
|
||||
}
|
||||
|
||||
Cart.prototype.resetDiscount = function() {
|
||||
this.setDiscount(0);
|
||||
|
@ -644,6 +660,7 @@ $.fn.inputAmount = function(obj, type) {
|
|||
|
||||
obj.updateSummaryTotal();
|
||||
obj.updateAmount();
|
||||
obj.updatePosData();
|
||||
obj.emptyCartToggle();
|
||||
});
|
||||
}
|
||||
|
@ -668,4 +685,4 @@ $.fn.removeAmount = function(obj, type) {
|
|||
obj.updateSummaryTotal();
|
||||
obj.emptyCartToggle();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue