Enhance PosData Viewer & add cart to posdata in POS app (#559)

This commit is contained in:
Andrew Camilleri 2019-01-26 05:26:49 +01:00 committed by Nicolas Dorier
parent 7ea665d884
commit 30bdfeee37
8 changed files with 96 additions and 50 deletions

View file

@ -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>();

View file

@ -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 });
}

View file

@ -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());

View file

@ -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; }
}
}

View file

@ -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>

View file

@ -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>
}

View 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>

View file

@ -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();
});
}
}