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() public void PosDataParser_ParsesCorrectly()
{ {
var testCases = var testCases =
new List<(string input, Dictionary<string, string> expectedOutput)>() new List<(string input, Dictionary<string, object> expectedOutput)>()
{ {
{ (null, new Dictionary<string, string>())}, { (null, new Dictionary<string, object>())},
{("", new Dictionary<string, string>())}, {("", new Dictionary<string, object>())},
{("{}", new Dictionary<string, string>())}, {("{}", new Dictionary<string, object>())},
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})}, {("non-json-content", new Dictionary<string, object>(){ {string.Empty, "non-json-content"}})},
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})}, {("[1,2,3]", new Dictionary<string, object>(){ {string.Empty, "[1,2,3]"}})},
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})}, {("{ \"key\": \"value\"}", new Dictionary<string, object>(){ {"key", "value"}})},
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})}, {("{ \"key\": true}", new Dictionary<string, object>(){ {"key", "True"}})},
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}", {("{ invalidjson file here}", new Dictionary<string, object>(){ {String.Empty, "{ invalidjson file here}"}})}
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
}; };
testCases.ForEach(tuple => testCases.ForEach(tuple =>
@ -1663,18 +1661,16 @@ donation:
var controller = tester.PayTester.GetController<InvoiceController>(null); var controller = tester.PayTester.GetController<InvoiceController>(null);
var testCases = var testCases =
new List<(string input, Dictionary<string, string> expectedOutput)>() new List<(string input, Dictionary<string, object> expectedOutput)>()
{ {
{ (null, new Dictionary<string, string>())}, { (null, new Dictionary<string, object>())},
{("", new Dictionary<string, string>())}, {("", new Dictionary<string, object>())},
{("{}", new Dictionary<string, string>())}, {("{}", new Dictionary<string, object>())},
{("non-json-content", new Dictionary<string, string>(){ {string.Empty, "non-json-content"}})}, {("non-json-content", new Dictionary<string, object>(){ {string.Empty, "non-json-content"}})},
{("[1,2,3]", new Dictionary<string, string>(){ {string.Empty, "[1,2,3]"}})}, {("[1,2,3]", new Dictionary<string, object>(){ {string.Empty, "[1,2,3]"}})},
{("{ \"key\": \"value\"}", new Dictionary<string, string>(){ {"key", "value"}})}, {("{ \"key\": \"value\"}", new Dictionary<string, object>(){ {"key", "value"}})},
{("{ \"key\": true}", new Dictionary<string, string>(){ {"key", "True"}})}, {("{ \"key\": true}", new Dictionary<string, object>(){ {"key", "True"}})},
{("{ \"key\": \"value\", \"key2\": [\"value\", \"value2\"]}", {("{ invalidjson file here}", new Dictionary<string, object>(){ {String.Empty, "{ invalidjson file here}"}})}
new Dictionary<string, string>(){ {"key", "value"}, {"key2", "value,value2"}})},
{("{ invalidjson file here}", new Dictionary<string, string>(){ {String.Empty, "{ invalidjson file here}"}})}
}; };
var tasks = new List<Task>(); var tasks = new List<Task>();

View file

@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NBitpayClient; using NBitpayClient;
using Newtonsoft.Json.Linq;
using YamlDotNet.RepresentationModel; using YamlDotNet.RepresentationModel;
using static BTCPayServer.Controllers.AppsController; using static BTCPayServer.Controllers.AppsController;
@ -155,10 +156,11 @@ namespace BTCPayServer.Controllers
var store = await _AppsHelper.GetStore(app); var store = await _AppsHelper.GetStore(app);
var title = settings.Title; var title = settings.Title;
var price = request.Amount; var price = request.Amount;
ViewPointOfSaleViewModel.Item choice = null;
if (!string.IsNullOrEmpty(request.ChoiceKey)) if (!string.IsNullOrEmpty(request.ChoiceKey))
{ {
var choices = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency); 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) if (choice == null)
return NotFound("Incorrect option provided"); return NotFound("Incorrect option provided");
title = choice.Title; title = choice.Title;
@ -211,7 +213,8 @@ namespace BTCPayServer.Controllers
string orderId, string orderId,
string notificationUrl, string notificationUrl,
string redirectUrl, string redirectUrl,
string choiceKey) string choiceKey,
string posData = null)
{ {
var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale); var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale);
if (string.IsNullOrEmpty(choiceKey) && amount <= 0) if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
@ -227,10 +230,11 @@ namespace BTCPayServer.Controllers
} }
string title = null; string title = null;
var price = 0.0m; var price = 0.0m;
ViewPointOfSaleViewModel.Item choice = null;
if (!string.IsNullOrEmpty(choiceKey)) if (!string.IsNullOrEmpty(choiceKey))
{ {
var choices = _AppsHelper.Parse(settings.Template, settings.Currency); 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) if (choice == null)
return NotFound(); return NotFound();
title = choice.Title; title = choice.Title;
@ -249,7 +253,7 @@ namespace BTCPayServer.Controllers
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id)); store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice() var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice()
{ {
ItemCode = choiceKey ?? string.Empty, ItemCode = choice?.Id,
ItemDesc = title, ItemDesc = title,
Currency = settings.Currency, Currency = settings.Currency,
Price = price, Price = price,
@ -258,6 +262,7 @@ namespace BTCPayServer.Controllers
NotificationURL = notificationUrl, NotificationURL = notificationUrl,
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(), RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
FullNotifications = true, FullNotifications = true,
PosData = string.IsNullOrEmpty(posData) ? null : posData
}, store, HttpContext.Request.GetAbsoluteRoot()); }, store, HttpContext.Request.GetAbsoluteRoot());
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id }); 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.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore.Internal;
using NBitcoin; using NBitcoin;
using NBitpayClient; using NBitpayClient;
using NBXplorer; using NBXplorer;
@ -679,9 +680,9 @@ namespace BTCPayServer.Controllers
public class PosDataParser 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)) if (string.IsNullOrEmpty(posData))
{ {
return result; return result;
@ -689,7 +690,6 @@ namespace BTCPayServer.Controllers
try try
{ {
var jObject =JObject.Parse(posData); var jObject =JObject.Parse(posData);
foreach (var item in jObject) foreach (var item in jObject)
{ {
@ -697,7 +697,14 @@ namespace BTCPayServer.Controllers
switch (item.Value.Type) switch (item.Value.Type)
{ {
case JTokenType.Array: 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; break;
default: default:
result.Add(item.Key, item.Value.ToString()); result.Add(item.Key, item.Value.ToString());

View file

@ -143,6 +143,6 @@ namespace BTCPayServer.Models.InvoicingModels
public DateTimeOffset MonitoringDate { get; internal set; } public DateTimeOffset MonitoringDate { get; internal set; }
public List<Data.InvoiceEventData> Events { get; internal set; } public List<Data.InvoiceEventData> Events { get; internal set; }
public string NotificationEmail { 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"> <div class="modal-footer bg-light">
<form method="post" asp-antiforgery="false" data-buy> <form method="post" asp-antiforgery="false" data-buy>
<input id="js-cart-amount" class="form-control" type="hidden" name="amount"> <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> <button id="js-cart-pay" class="btn btn-primary btn-lg" type="submit"><b>@Model.CustomButtonText</b></button>
</form> </form>
</div> </div>

View file

@ -192,24 +192,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h3>Point of Sale Data</h3> <h3>Point of Sale Data</h3>
<table class="table table-sm table-responsive-md"> <partial name="PosData" model="@Model.PosData"></partial>
@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>
</div> </div>
</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.updateItemsCount();
this.updateAmount(); this.updateAmount();
this.updatePosData();
} }
Cart.prototype.setCustomAmount = function(amount) { Cart.prototype.setCustomAmount = function(amount) {
@ -243,6 +244,7 @@ Cart.prototype.updateAll = function() {
this.updateSummaryTotal(); this.updateSummaryTotal();
this.updateTotal(); this.updateTotal();
this.updateAmount(); this.updateAmount();
this.updatePosData();
} }
// Update number of cart items // Update number of cart items
@ -290,6 +292,20 @@ Cart.prototype.updateTip = function(amount) {
Cart.prototype.updateAmount = function() { Cart.prototype.updateAmount = function() {
$('#js-cart-amount').val(this.getTotal(true)); $('#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() { Cart.prototype.resetDiscount = function() {
this.setDiscount(0); this.setDiscount(0);
@ -644,6 +660,7 @@ $.fn.inputAmount = function(obj, type) {
obj.updateSummaryTotal(); obj.updateSummaryTotal();
obj.updateAmount(); obj.updateAmount();
obj.updatePosData();
obj.emptyCartToggle(); obj.emptyCartToggle();
}); });
} }